hold-to-confirm
v0.2.0
Published
A press-and-hold confirmation button for React: fill or ring progress, keyboard and touch support, zero required styling framework.
Maintainers
Readme
hold-to-confirm
A press-and-hold confirmation button for React. Built for destructive or high-stakes actions (delete, power off, send) where a plain click is too easy to trigger by accident.
Installation
npm install hold-to-confirmUsage
import { HoldButton } from 'hold-to-confirm'
function DeleteRow({ id }: { id: string }) {
return (
<HoldButton onConfirm={() => deleteItem(id)}>
Hold to delete
</HoldButton>
)
}Icon-only buttons work the same way, using the ring variant:
<HoldButton variant="ring" onConfirm={shutDown} aria-label="Hold to power off">
<PowerIcon />
</HoldButton>Show something different once confirmed. The package doesn't have an opinion on what "done" looks like, so it's entirely up to you:
<HoldButton onConfirm={() => deleteItem(id)} confirmedChildren="✓ Deleted.">
Hold to delete
</HoldButton>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| onConfirm | () => void | required | Fires once, when the hold completes |
| duration | number (ms) | 1500 | Time required to hold to confirm |
| variant | 'fill' \| 'ring' | 'fill' | fill sweeps across the button (best for text buttons); ring draws a ring around it (best for icon-only buttons) |
| confirmThreshold | number (0–1) | 1 | Fraction of duration after which onConfirm fires |
| color | string | #2563eb | Progress color |
| confirmedChildren | ReactNode | | Shown instead of children once confirmed (e.g. a checkmark + "Deleted."). Omit it and children just keeps showing, as before |
| disabled | boolean | false | |
| onCancel | () => void | | Fires on early release/blur/pointer-cancel |
| onProgress | (progress: number) => void | | Raw 0–1 progress, for driving your own UI |
| className / style | | | Applied to the root <button> |
After a successful confirm, the button becomes disabled, the fill/ring
settles to a fuller opacity of your color, and if confirmedChildren swaps
in it fades in over 180ms rather than popping in instantly. There's no
built-in reset: render a new instance (e.g. via key) if you need to allow
another hold.
Accessibility
- Works with mouse, touch, and keyboard (
Space/Enterto hold). aria-busy="true"while holding.- If the currently-visible content (
confirmedChildrenonce confirmed,childrenotherwise) isn't plain text and you haven't passed your ownaria-label, one is generated automatically so the button is never left unlabeled. This text-detection only understands plain strings/numbers, not nested elements, so an icon-and-textconfirmedChildrencombo (like<><Check /> Deleted.</>) won't be picked up automatically; either pass it as a single string, or supply your ownaria-labelalongside it. - Respects
prefers-reduced-motion: the progress cue becomes a quick fade instead of a sweep animation, and theconfirmedChildrenfade-in is skipped (the content still swaps, just instantly). The actual required hold time is unchanged either way.
License
MIT
