When building buttons with a loading state in React, a common pattern many developers use is conditional rendering:
{pending ? <Loader /> : "Submit"}
At first glance, this works. But there’s a subtle (and frustrating) UI issue: The button shrinks or changes size when toggling between the text and the loader. This causes layout shift and makes your interface feel unpolished.
The Problem
Imagine you have a button like this:
<button>
{pending ? <Loader /> : "Submit"}
</button>
When pending
becomes `true, the text disappears and the loader appears.
But unless your loader has exactly the same width as your button text, the button will resize — often noticeably.

The Solution - Layer, Don't Swap
Instead of conditionally rendering one or the other, we can stack both elements using CSS grid and control visibility. This keeps the layout intact and provides a smooth experience.
Here’s how to do it with Tailwind CSS:
<button className="grid [grid-template-areas:'stack']">
<Loader
className={`[grid-area:stack] m-auto ${pending ? 'visible' : 'invisible'}`}
/>
<span
className={`[grid-area:stack] ${pending ? 'invisible' : 'visible'}`}
>
Submit
</span>
</button>
Why this works:
- Both the text and loader are always present in the DOM
- They're positioned in the exact same spot using grid area
stack
- We just toggle their visibility, so the button dimensions stay fixed
Wrap Up
A smoother UI doesn’t always require complex code; sometimes, it’s as simple as switching from conditional rendering to toggling visibility.