Hold to confirm
A deliberate action, held for a moment: the ring fills, the label changes, and confirmation settles into a check.
Install with the shadcn CLI
Overview
Give an important action a little intention without opening another dialog. Press and hold while a fine progress ring traces around the icon. Release early and it returns to rest. Cross the threshold and the same button moves through pending, success or error, with a morphing label and drawn check. The action runs once, and an error can be retried with another hold.
States
- hold
- The icon ring fills linearly over the configured threshold (1100ms by default). A short press does not confirm.
- release
- Early release, Escape, focus loss or pointer interruption cancels the hold. The ring resets over 240ms.
- pending
- After the threshold, the callback runs once. The label morphs and the progress icon gives way to a spinner.
- success / error
- A resolved callback draws a check; a rejected callback shows an alert and offers another hold. The button resets after 2200ms by default.
Usage
Depends on lucide-react, installed for you by the CLI along with the keyframes it needs.
Works with
Connect a deliberate action
The callback owns the actual operation. Reject its promise to show an error; the component never invents a successful result.
Props
| Prop | Type | Description |
|---|---|---|
| onConfirm | () => void | Promise<void> | Runs once after the hold threshold. Resolves to success or rejects to error. |
| duration | numberdefault 1100 | Required hold in milliseconds, clamped to 300–5000. |
| resetAfter | numberdefault 2200 | Delay before returning to idle. Set 0 to keep the result visible. |
| labels | Partial<Record<HoldStatus, string>> | Labels for idle, holding, pending, success and error. |
| icon | ReactNode | Idle and holding icon. Defaults to a fingerprint. |
| disabled | booleandefault false | Disables the action and cancels an active hold. |
Accessibility and motion
- Hold the pointer, Space or Enter to confirm. Key repeat does not restart the timer, and a normal click never triggers the callback.
- Releasing early, leaving the button, losing focus, Escape or a hidden browser tab cancels an incomplete hold. A completed action continues through its promise.
- State announcements live outside the button. The button retains focus while pending or showing its result.
- Reduced motion removes icon movement and spinning. The functional progress ring still communicates the hold duration.
- Use clear action labels and provide undo in your application where appropriate.
Requirements
React 19, Tailwind CSS v4 and shadcn/ui theme variables (any style, light and dark). Icons from lucide-react.
The CLI also installs Text morph.
Source
Registry item: /r/hold-to-confirm.json