Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Buttons

Pro

Long Press Button for React

A button that confirms only after it's held, with a fill that grows while it's pressed. For React with Base UI, when one tap is too easy for an action you can't undo.

Framework
UI library

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.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

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.

Preview width

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.

Preview width

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.

How do I show that it's done?

In onConfirm, set your own state and show it, as the first demo does with the line below the button, or pass isLoading while the action runs. The fill stays full until the button is released.

What happens with reduced motion?

The fill doesn't sweep: it covers the button and fades in over duration instead, and the hold still has to last that long.

API Reference

LongPressButton

data-slot="long-press-button-fill"

PropType
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.

CSS variableType
--long-press-button-durationstring

How long the hold lasts (1000ms).

--long-press-button-fillstring

The fill's color.

--long-press-button-heldstring
All 35 buttons