Skip to content
WANCRAFT

A.03Buttons & Controls

Hold to Confirm

A destructive-action button that fires only after a sustained press, with a rewinding progress ring.

ReactMotionSVGTailwindintermediate

Overview

Hold to Confirm replaces “Are you sure?” dialogs with a deliberate gesture. Pressing with a pointer, or holding Space or Enter, drives an SVG progress ring and a soft wash behind the label. Releasing early rewinds the ring; completing it calls onConfirm, draws a check mark and announces the result, then resets after a short delay. Progress is a motion value animated linearly, so nothing re-renders while the ring fills.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/hold-to-confirm.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/hold-to-confirm.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/hold-to-confirm.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
1 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { HoldToConfirm } from "@/components/ui/hold-to-confirm";

export function HoldToConfirmExample() {
  return (
    <HoldToConfirm onConfirm={() => console.log("Confirmed")}>
      Hold to delete
    </HoldToConfirm>
  );
}

03Props

PropTypeDefaultDescription
onConfirm() => void—Called once when the hold completes.
holdDurationnumber1500How long the press must be held, in ms.
resetDelaynumber2000How long the confirmed state is shown before resetting, in ms.
confirmedLabelstring"Deleted"Label shown and announced after confirmation.
childrenReactNode"Hold to delete"Label (and accessible name) before confirmation.
accentstring"#FF7448"Progress ring, wash, confirmed background and focus ring colour.
inkColorstring"#0B0D0F"Label and icon colour on the accent once confirmed.
variant"pill" | "circle""pill"Pill with a label, or a round icon button with a caption.
iconReactNode—Glyph inside the ring. Defaults to a trash icon.
hintstring"Press and hold to confirm. Release to cancel."Instruction exposed via aria-describedby.
onHoldStart / onCancel() => void—Called when a hold starts / is released early.
...propsButtonHTMLAttributes—Other native button attributes (except onClick) are forwarded; the ref points to the <button>.

04Features

  • 01SVG progress ring driven by a motion value — no re-render while holding
  • 02Early release rewinds the ring; re-pressing resumes from the current progress
  • 03Pointer capture keeps the hold alive if the finger drifts
  • 04Check mark draws in on confirmation, then the button resets after a delay
  • 05Pill and circular variants
motion · tweenmotion · springinput · pressinput · keyboardinput · pointer

05Accessibility

  • Native <button>; holding Space or Enter behaves exactly like holding the pointer (key repeat is ignored).
  • The hold instruction is linked with aria-describedby.
  • A polite live region announces holding, cancellation and the confirmed label.
  • aria-disabled is set while showing the confirmed state; the visible focus ring uses the accent.
  • With prefers-reduced-motion the press scale and rewinds are removed; the linear progress ring remains as essential feedback.

06Limitations

  • onClick is intentionally not forwarded — use onConfirm so a single tap can never trigger the action.