shadcn.io is not affiliated with official shadcn/ui
Press and hold, and a fill sweeps across the button. When it reaches the end, onConfirm runs once. Let go early, or move focus away, and the fill eases back and nothing happens.
Use it for actions that cost real time when triggered by mistake: delete a project, erase a device, sign out everywhere. For everyday actions a hold is just slow, so keep a plain button. It wraps the Button, so variant and size work as usual. The fill is a CSS transition that runs while the button is pressed, and the hold confirms when it ends full, with no animation library and no timer. With reduced motion the fill fades in instead of sweeping, and the hold is still required.
Installation
Usage
import { LongPressButton } from "@/components/ui/long-press-button";<LongPressButton onConfirm={deleteProject} variant="destructive">
Hold to delete
</LongPressButton>Demos
Duration
duration is how long the hold takes, in milliseconds, and the default is 1000. Shorten it for actions that are easy to undo, such as archiving, and lengthen it for ones that aren't, such as erasing a device.
Fill Color
fillColor is the fill's color, any CSS color. By default it's the label's color at a fifth of its strength, which reads on every variant. Here an outline button fills with a quarter of the primary color and a ghost button with a fifth of the destructive one; keep it see-through so the label stays readable.
Accessibility
The button is announced with its label and a description, Press and hold to confirm, so people who can't see the fill know a quick press won't do it. Change the description with hint. Holding Enter or Space works the same as holding the pointer down, and releasing the key, or moving focus away, cancels. The fill is decoration and hidden from assistive tech.
- Enter
- Hold to fill the button; release early to cancel.
- Space
- Hold to fill the button; release early to cancel.
FAQ
Why does my action run on a quick tap?
Because it's on the press handler. Put it on onConfirm, which only runs when the hold lasts the full duration; a click never calls it.
A click handler still fires on every click, held or not, including the release at the end of a hold, as on any button: use it for analytics, not for the action.
How long should the hold be?
The default of one second suits most destructive actions. Use 1.5 to 2 seconds for ones you can't undo at all, and around 0.6 seconds when the action is easy to reverse. The Duration demo shows 600 and 2000.
What cancels a hold?
Letting go before the end, the browser taking over the touch to scroll, or focus leaving the button. The fill eases back and onConfirm doesn't run.
Sliding the pointer off while it's still down doesn't cancel: the browser keeps the button pressed until you let go.
Can a long hold confirm twice?
No. onConfirm runs once per hold, when the fill becomes full, and it stays full until the button is released. A held key's repeats don't restart it.
API Reference
LongPressButton
data-slot="long-press-button-fill"
children?ReactNode
The label, above the fill.
duration?number
How long the button has to be held, in milliseconds, before
onConfirm runs.
- Default
1000
fillColor?string
The fill that grows while the button is held, any CSS color. By default the label's color at a fifth of its strength.
focusableWhenDisabled?boolean
Whether the button should be focusable when disabled.
- Default
false
hint?string
Read by screen readers as the button's description, to say it needs a hold rather than a press. Pass an empty string to leave it out.
- Default
"Press and hold to confirm"
nativeButton?boolean
Whether the component renders a native <button> element when replacing it
via the render prop.
Set to false if the rendered element is not a button (for example, <div>).
- Default
true
onConfirm?() → void
Called once when a hold lasts the full duration. Letting go early
cancels; a plain press never calls it.
- Returns
void
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
--long-press-button-durationstring
How long the hold lasts (1000ms).
--long-press-button-fillstring
The fill's color.
--long-press-button-heldstringRelated
- Pro
Slide To Confirm Button
ControlsDrag to confirm instead of holding
Countdown Button
Progress & resultsA timed confirm with time to cancel
- Pro
Shake Button
Motion & feedbackSignal a refused or failed action
- Pro
Success Button
Progress & resultsShow the action finished
Loading Button
Progress & resultsProgress after the hold completes
Record Button
Progress & resultsAnother press-and-hold control