One of the easiest ways to make a product feel less reliable is to let a loading button change width.
The user clicks Save changes, the label flips to
Saving..., and suddenly the button shrinks or grows. The
secondary action moves. The footer rhythm changes. The exact control that
should feel most stable now looks like it is reflowing under pressure.
That is a small detail, but it lands at the worst possible moment. The user just committed an action and is waiting for confirmation that the product has it under control.
Loading feedback is supposed to answer one question: did the action register?
If the button changes its outer footprint at the same time, it creates a second, unnecessary signal. Now the UI is not just busy. It is also moving around. That makes the state change feel less intentional and more accidental.
This matters most when the button lives in a tight layout:
In all of those places, button width is part of the layout, not just part of the label.
If the action is the same button before and during loading, the button should usually keep the same outer width.
Change the content inside the shell. Do not let the shell resize itself.
That means:
The user still gets feedback. They just do not pay for it with layout shift.
One of the cleanest patterns is to preserve width with an invisible copy of the widest label, then overlay the active content on top.
<button
class="relative inline-grid min-h-11 place-items-center rounded-full bg-black px-4 py-2 text-sm font-medium text-white"
>
<span class="invisible inline-flex items-center gap-2">Save changes</span>
<span class="absolute inset-0 inline-flex items-center justify-center gap-2">
<span
class="h-3 w-3 animate-spin rounded-full border-2 border-white/35 border-t-white"
></span>
Saving...
</span>
</button>
The invisible span defines the width. The absolute layer handles the visible state. The button stays stable.
You can do the same thing with CSS grid, a fixed min-width,
or two overlaid spans. The specific technique matters less than the rule:
keep the outer dimensions calm.
This is especially valuable when the loading state is short.
If the request takes 300 to 800 milliseconds, users might barely read the label change. What they will notice is the button jumping and the neighboring controls shifting with it.
A stable button gives you the best version of the interaction:
That is usually enough.
If the button is still the same button, treat loading as a content swap, not a layout change.
That one habit makes button loading states feel much more production-ready.