shadcn.io is an independent project built on shadcn/ui
Buttons
ProSuccess Button for Vue
Goes from its label to a spinner to a check that draws itself in. For Vue with Ark UI, when a save or a send needs confirming without a toast.
Drive it with state: idle shows the label, loading a spinner with Loading…, and success a check with Done. The check pops in and draws itself. After resetDelay, two seconds by default, it calls onStateChange("idle") so you can set it back.
Use it for actions people want confirmed in place: saving settings, adding to a cart, sending an invite. For a long list of results or an error, a toast or an inline message says more; for a wait with no confirmation, the Loading Button is enough. It wraps the Button, so variant and size work as usual. The rows share one place and swap with CSS transitions, and the check's stroke is a CSS keyframe animation the install adds to your stylesheet, with no animation library; with reduced motion the rows only fade and the check appears whole.
Installation
Usage
<script setup lang="ts">
import { SuccessButton } from "@/components/ui/success-button";
</script>
<SuccessButton
onClick={save}
onStateChange={setState}
state={state}
successLabel="Saved"
>
Save changes
</SuccessButton>Demos
Color
successColor colors the check, any CSS color; by default it takes the label's color. Here an outline button's check turns green once the item is in the cart.
Staying Done
resetDelay={0} keeps the check until you set state back to idle yourself. It suits an action that happens once, like sending an invite.
Accessibility
The spinner, the check and their rows are hidden from assistive tech; the label at rest keeps naming the button. A polite status inside the button says loadingLabel when it starts and successLabel when it's done. While loading, the button is marked aria-disabled and ignores presses, but keeps its focus.
- Enter
- Activate the button, unless it's loading.
- Space
- Activate the button, unless it's loading.
FAQ
How do I show the check?
Set state="loading" when your request starts and state="success" when it resolves. The button doesn't run the request; it shows where it is.
Does it go back to the label by itself?
It asks to: resetDelay milliseconds after success, 2000 by default, it calls onStateChange("idle"). Pass your state's setter there. With resetDelay={0}, the check stays until you change state.
Can I change Loading… and Done?
Yes, with loadingLabel and successLabel, such as Saving… and Saved. The button is as wide as the widest of the three rows, so it doesn't change size between them.
Can the success state use another color?
successColor colors the check, such as var(--color-emerald-600). The rest keeps the variant's colors, so the label stays readable on every variant. The Color demo uses it on an outline button.
Can it be pressed while it loads?
No. While state is loading it's aria-disabled, presses are ignored, and it stays focusable. In the success state it can be pressed again.
Your click handler isn't called while it loads.
API Reference
SuccessButton
data-slot="success-button-label"
state?SuccessButtonState
Where the action is: at rest, running (a spinner, and presses stop) or done (a check that draws itself). You set it from your request.
- Default
"idle"
children?VNode
The label at rest; it names the button in every state.
clickEffect?boolean
Apply a click effect to the button.
- Default
true
isLoading?boolean
Show a loading indicator.
- Default
false
loadingLabel?VNode
The label while state is "loading", beside the spinner.
- Default
"Loading…"
resetDelay?number
How long the check stays before onStateChange("idle"), in
milliseconds; 0 keeps it until you change state.
- Default
2000
successColor?string
The check's color, any CSS color. By default the label's color.
successLabel?VNode
The label once state is "success", beside the check.
- Default
"Done"
asChild?boolean
Use the provided child element as the default rendered element, combining their props and behavior.
class?string
Merged with the part's classes.
@state-change(next: SuccessButtonState) → void
Called with "idle" when the check has stayed resetDelay milliseconds.
- Type
(next: SuccessButtonState) → void- Parameters
next– SuccessButtonState- Returns
void
data-state?string
Mirrors state.
--success-button-colorstring
The check's color (the label's by default).





