Hold Button
ProPress and hold to confirm, for the actions a stray tap must never set off: a panic call, ending a call for everyone, wiping a device. The fill is a timed Progress running on the compositor, and letting go early cancels with a shake.
Installation#
Usage#
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Variants#
neutral (the default) sits on the secondary surface and fills with an ink tint. destructive is for panic-like actions: Button's soft red at rest, a deeper tint sweeping in while held, and the solid red once it fires. Both turn solid on confirm and stay that way until released, so the hold that did it is unmistakable.
Sizes#
md (the default) is 40px, the hit target on its own; lg is 48px, for the one action a panel is about. Give it className="w-full" to span a row.
Hold time and callbacks#
holdMs sets how long the hold must last (1000ms by default). onConfirm fires the moment it has, while the button is still held; a release before that fires onCancel(ms) with the time held, and the control shakes. Sliding the pointer off the button, tabbing away and the window losing focus all end the hold the same way. Here the hold is two seconds.
In a dialog#
The confirm step of a destructive dialog. The dialog says what is about to go; the hold makes sure the answer was meant. Close the dialog from onConfirm.
Keys#
keys picks which keys hold the focused button, as KeyboardEvent.key names ("Space" is accepted for " "). Space and Enter by default; ["Enter"] leaves Space to a surrounding list that uses it, and [] turns the keyboard hold off for a parent that drives it.
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Controlled#
A parent that owns the keyboard (a tablet whose list runs on arrow keys, a game binding) drives the hold with holding and onHoldingChange, the same pair as a dialog's open. true starts a hold and false ends it, confirmed or cancelled exactly as a release. Controlled, the button's own gestures (pointer, keys, sliding off, a lost window) only ask through onHoldingChange and wait for the prop; disabling it still ends the hold. onHoldStart fires whenever a hold begins, in both modes. Here the page listens for H itself.
Disabled#
disabled dims the control, and disabling it mid-hold ends the hold without confirming. A one-shot action can disable itself from onConfirm.
Accessibility#
It is a native <button>: Space or Enter held on it is the same hold as a pointer, and Enter never clicks it through. The button is described by a hint, "Press and hold for 1 second" by default, phrased from holdMs; pass hint to translate it. Your own aria-describedby is kept after it. The fill is a timed Progress hidden from assistive tech, and under reduced motion it ticks instead of gliding. Some screen readers activate a button with a single synthetic click, which can never be a hold: for an action people must be able to reach that way, offer a second path too (a menu item with a confirm dialog).
API reference#
HoldButton
A <button> with data-state="idle" | "holding" | "confirmed". Props: holdMs (default 1000), onConfirm(), onCancel(heldMs), onHoldStart(), holding and onHoldingChange(holding) (controlled hold), keys (default [" ", "Enter"]), hint, variant (neutral · destructive), size (md · lg) and disabled. Children are the label (an icon is sized like Button's). Every button prop is forwarded; className is merged last. The fill is a Progress inside it, reachable as [data-slot=progress-indicator].