Async button
The button for anything that runs an action: saving, sending, paying, deleting. It runs submitAction in its own transition, swaps the label for a spinner in place, then shows how it went. The caller keeps no pending state (no isLoading, no useTransition); it only returns the outcome. The one above succeeds and fails in turn, so press it twice.
Examples
Success
Returning "success" draws a check beside successLabel for a moment before the button settles. Use it where the result would otherwise be invisible, like a copied link.
Error
The caller says what went wrong (a toast), then returns "error". The button shakes, draws a cross beside errorLabel, and offers to try again a moment later.
Pending label
A wait long enough that a bare spinner reads as stuck gets a word beside it. Returning nothing means done with nothing to show, usually because a navigation follows.
Confirm
confirm asks first: the button opens an alert dialog and its "Yes" runs the action. The dialog stays open while the action runs, closes when it lands, and stays open on "error" so "Yes" can shake and be pressed again; Enter confirms. It fails every other time here.
API reference
Every Button prop applies too; with confirm they style the trigger.
| Prop | Type | Default | Description |
|---|---|---|---|
| submitAction | () => "success" | "error" | undefined | Promise<"success" | "error" | undefined | void> | — | Runs in the button's own transition. Say what went wrong yourself before returning "error". |
| successLabel | ReactNode | — | Shown with a check after "success". Defaults to the label. |
| errorLabel | ReactNode | — | Shown with a cross after "error". Defaults to the label. |
| pendingLabel | ReactNode | — | Shown beside the spinner, for a wait long enough that a spinner alone reads as stuck. |
| hideIcon | boolean | false | Text only, no check or cross. |
| loading | boolean | false | Shows the spinner for work the button doesn't run itself, like a form submitting. |
| confirm | { title?: ReactNode; description: ReactNode; className?: string } | — | Ask first. The title defaults to "Are you sure?"; className sizes the dialog. |