Hold to Confirm
A press-and-hold fill confirmation button: holding fills the progress from left to right with a slight tremor near completion, and only a full fill triggers it — preventing accidental taps on dangerous actions.
npx shadcn@latest add https://webberui.com/r/hold-to-confirm.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<HoldToConfirm />
Installation
npx shadcn@latest add https://webberui.com/r/hold-to-confirm.jsonInstalling also writes the --wb-duration-fast CSS variable into your global stylesheet.
Usage
import { HoldToConfirm } from "@/components/ui/hold-to-confirm";
<HoldToConfirm
onConfirm={() => deleteAccount()}
confirmedChildren="Deleted"
>
Hold to delete account
</HoldToConfirm>Props
| Prop | Type | Default | Description |
|---|---|---|---|
holdDuration | number | 1.2 | How long (seconds) it must be held to confirm; the fill completes at a constant rate over this duration |
onConfirm | () => void | — | Fires when the fill completes, exactly once per success |
confirmedChildren | ReactNode | — | Content shown after a successful confirmation; falls back to children when not provided |
variant | 'danger' | 'default' | 'danger' | danger is the red scheme (for dangerous actions), default is neutral |
className | string | — | Overrides the default styles |
children | ReactNode | — | Button content |
All other <button> attributes (disabled, aria-*, and so on) are forwarded directly. A click never confirms — holding is the only confirmation gesture.
How it works
- Constant-rate fill: on pointer down (or holding the space bar) the fill layer sweeps from left to right with
scaleXat a linear rate; if you let go early it stops midway, and pressing again resumes from the current progress at the same rate - Tremor near the threshold: past 65% the button starts shaking slightly from side to side, with the amplitude growing with progress to build tension for "about to take effect"
- Spring retreat: on an early release the fill springs back to 0 quickly (
stiffness: 500, damping: 42) - Success state: a full fill triggers
onConfirm, the button turns green, a checkmark draws itself in, andconfirmedChildrenis shown. The component stays in the success state afterwards; to replay it, change thekeyto remount (see the demo) - Keyboard support: space bar keydown starts the fill and keyup cancels it, matching the mouse press-and-hold behavior;
e.repeatis filtered out so holding it down does not fire repeatedly - Accessibility: a "hold to confirm" description is attached with
aria-describedby, and success is announced througharia-live="polite" - When the user has "reduce motion" enabled at the system level: the hold duration and the linear fill are kept exactly as they are (functionality unchanged), and only the tremor is removed, with the retreat becoming a fast linear one
touch-none+select-noneprevent a touch hold from triggering scrolling or text selection; the pointer leaving the button, the event being cancelled (pointercancel), or losing focus during a keyboard hold all count as a release and cancel it
Play/Pause Morph
A graphical button that morphs smoothly between a play triangle and pause bars, with controlled and uncontrolled modes and an optional progress ring.
Gooey Button
A liquid gooey button: droplets at the bottom fuse through an SVG goo filter, well up to cover the button on hover, and splash out thick droplets at the click point.