Hold to confirm
Confirm a destructive action by holding, not tapping.
holdlong pressconfirm
Preview
Hold to confirm preview
Live · keyboard ready
Press and hold for 1.2 seconds to confirm. With a keyboard, hold Space or Enter.
When to use
- Destructive actions like delete or revoke where the consequence is already visible on screen.
- Replacing a confirm dialog with a deliberate press in compact settings rows.
- Actions that should not fire on an accidental tap on touch devices.
When not to use
- Use dialog when people need to read what will happen before confirming.
- Use button or action-button for safe, easily undone actions.
- Use swipe-actions for delete in touch lists.
shadcn CLI
Also installs (1)
- manicat-foundation
Manual install(+)
registry.jsonjson
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "hold-to-confirm",
"type": "registry:ui",
"title": "Hold to confirm",
"description": "Confirm a destructive action by holding, not tapping.",
"categories": [
"actions"
],
"dependencies": [
"lucide-react",
"motion"
],
"registryDependencies": [
"https://manicat.viraza.net/r/manicat-foundation.json"
],
"files": [
{
"path": "registry/components/hold-to-confirm/hold-to-confirm.tsx",
"type": "registry:component",
"target": "@components/manicat/hold-to-confirm/hold-to-confirm.tsx"
},
{
"path": "registry/components/hold-to-confirm/hold-to-confirm.module.css",
"type": "registry:component",
"target": "@components/manicat/hold-to-confirm/hold-to-confirm.module.css"
}
],
"docs": "Docs and live preview: https://uiarc.dev/components/hold-to-confirm",
"meta": {
"tier": "free",
"kind": "component",
"docs": "https://manicat.viraza.net/components/hold-to-confirm",
"markdown": "https://uiarc.dev/components/hold-to-confirm/markdown",
"tags": [
"hold",
"long press",
"confirm"
]
}
}Usage
Install the component, then copy this into your app. Change the content and the props to fit your screen.
example.tsxtsx
"use client";
import { useEffect, useState } from "react";
import { HoldToConfirm } from "@/components/manicat/hold-to-confirm/hold-to-confirm";
export function HoldToConfirmExample() {
const [confirmed, setConfirmed] = useState(false);
return (
<div className="flex flex-wrap items-center gap-3">
<HoldToConfirm label="Hold to delete" onConfirm={() => setConfirmed(true)} />
{confirmed ? <span className="text-sm text-(color:--text-muted)">Deleted.</span> : null}
</div>
);
}
Examples
Real screens rather than isolated props. Switch to Code to copy a whole example into your project.
Hold to delete A destructive action that needs an intentional press, with the result shown next to it.
Hold to delete
A destructive action that needs an intentional press, with the result shown next to it. preview
Live · keyboard ready
Press and hold for 1.2 seconds to confirm. With a keyboard, hold Space or Enter.
hold-to-delete-a-destructive-action-that-needs-an-intentional-press-with-the-result-shown-next-to-it-.tsxtsx
"use client";
import { useState } from "react";
import { HoldToConfirm } from "@/components/manicat/hold-to-confirm/hold-to-confirm";
export function HoldToConfirmDeleteExample() {
const [confirmed, setConfirmed] = useState(false);
return (
<div className="flex flex-wrap items-center gap-3">
<HoldToConfirm label="Hold to delete account" onConfirm={() => setConfirmed(true)} />
{confirmed ? <span className="text-sm text-(color:--text-muted)">Account scheduled for deletion.</span> : null}
</div>
);
}
Keyboard interactions
| Keys | Action |
|---|---|
| Space / Enter (hold) | Fills the button; releasing early rewinds it. |
| Tab away | Cancels an active hold. |
Accessibility
- aria-describedby points to a hidden hint: "Press and hold for N seconds to confirm. With a keyboard, hold Space or Enter."
- aria-label tracks the visible label; a role="status" region announces confirmedLabel.
- Once done the button sets aria-disabled; the text is drawn twice so it changes color exactly at the fill edge.
Motion
- The fill advances on a linear timeline while held, rewinds on a smooth spring when released, and resumes from where it was on a quick re-press.
- The button presses to about 0.97; on completion the label morphs, the width springs, and a check draws itself in. Touch devices get a short vibration.
- Reduced motion drops the press scale and rewind spring and swaps label and icon with a fade; the fill still tracks the hold.
Responsive behavior
- Uses pointer events with capture, so mouse, pen, and touch holds all work; sliding more than 24px off the button cancels the hold.
- Touch holds trigger a short vibration on completion where the device supports it.
- Keyboard users hold Space or Enter, and a hidden hint explains the duration.
Performance
- The fill runs on a single linear motion animation, not a React state update per frame.
- The label is drawn twice to change color at the fill edge; a ResizeObserver handles the width spring on completion.
Notes for AI
- Use for destructive or hard-to-undo actions when the consequence is already visible on screen. Use dialog when people need to read what will happen first.
- Write the label as an instruction ("Hold to …"); pass confirmed to reset it after an undo or a failed request.
- Pair with onHoldChange to show a nearby hint while the press is held.