Action button
FreeA button that runs async work and reports progress, success or failure on itself, at one width.
pnpm dlx shadcn@latest add https://caliperui.site/r/action-button.json3 commits ready · last deploy 2 hours ago
When to use #
Use it for
- Save, send and sync actions that take a moment.
- You want the result shown on the control without a toast.
Skip it when
- The action is instant; use a button.
- The result needs a long explanation; show an alert.
Installation #
pnpm dlx shadcn@latest add https://caliperui.site/r/action-button.json
Adds its 3 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.
Usage #
This is the code behind the preview above.
import { useState } from "react" import { ActionButton } from "@/components/ui/action-button" export function SaveNote() { const [fail, setFail] = useState(false); const [progress, setProgress] = useState<number | undefined>(undefined); const [status, setStatus] = useState("3 commits ready · last deploy 2 hours ago"); function deploy() { setStatus("Uploading 214 files to eu-central-1…"); return new Promise<void>((resolve, reject) => { let value = 0; setProgress(0); const timer = setInterval(() => { value += 8; if (fail && value >= 62) { clearInterval(timer); setProgress(undefined); setStatus("Build failed at step 3 of 5: missing STRIPE_KEY. Add it in Settings, then retry."); reject(new Error("missing STRIPE_KEY")); return; } if (value >= 100) { clearInterval(timer); setProgress(undefined); setStatus("Live at halden.app · deployed 14:32."); resolve(); return; } setProgress(value); }, 90); }); } return ( <div className="grid justify-items-center gap-3"> <ActionButton label="Deploy to production" pendingLabel="Uploading" successLabel="Deployed" errorLabel="Retry deploy" progress={progress} onAction={deploy} /> <p role="status" className="max-w-xs text-center text-sm text-muted-foreground">{status}</p> <label className="flex items-center gap-2 text-xs text-muted-foreground"> <input type="checkbox" checked={fail} onChange={(event) => setFail(event.target.checked)} /> Make the build fail </label> </div> ); }
API reference #
ActionButton
component
Accessibility #
It is built on native elements and keeps aria-label, aria-busy, aria-hidden and aria-live in step with state. Changes that matter are announced through a live region.
No custom key handling; Tab and the native activation keys do the work.
Motion #
It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.