Components · Actions
Button
shadcn's Button with a loading state: a spinner over the label, and the button keeps its width.
Installation
npx shadcn@latest add https://www.syntaqx.com/r/button.jsonChanges from shadcn
It installs over shadcn's Button and keeps its API, variants and data-slots, so code written for it keeps working. On top of that:
- A
loadingprop: the label stays in place but hidden, so the button keeps its width, with a spinner over it andaria-busyset. - A busy button doesn't fade the way a disabled one does.
Examples
Sizes
Loading
Optimistic UI
Trust the input: apply the change immediately, reconcile on response, and roll back visibly on failure. The button is never disabled mid-flight, and a request id makes only the latest click's response count, so rapid clicks don't fight.
Inspired by Linear and Superhuman
Destructive-action guards
Calibrate the friction to the blast radius. A dismiss should be one click; a drop-database should require typing the name. Don't make a delete feel like an unsubscribe, or an unsubscribe feel like a delete.
Inspired by GitHub and Stripe
No guard
Reversible, low blast radius: dismiss a notification.
Soft confirm
Reversible within a window: delete a draft.
Type to confirm
Irreversible, high blast radius: drop a database, delete an org.