Hold To Commit Button
A destructive action that requires sustained pressure, with a ring that fills on an ease-in curve.
DOM + CSS1 dep1 file · 5 KB TSX
Props
"use client";
import { useCallback, useRef, useState } from "react";
import { Check, Trash2 } from "lucide-react";
type Phase = "idle" | "holding" | "done";
export function HoldToCommitButton({
label = "Hold to delete",
duration = 1400,
danger = "#e5484d",
onCommit,
}: {
label?: string;
duration?: number;
danger?: string;
onCommit?: () => void;
}) {
// ---------------------------------------------------------------
// Full source available with a viberdy Pro subscription.
// https://viberdy.dev/pricing
// ---------------------------------------------------------------
}Details
About this component
A confirmation dialog asks a question the user answers by reflex; a hold asks for sustained intent, which is much harder to give accidentally. The timing curve is the whole product here. Linear feels punitive because the user is just waiting out a timer, and ease-out feels finished well before it is, so people release early and nothing happens — the worst possible outcome for a control whose entire purpose is deliberate commitment. Ease-in is correct: slow to start, fast to finish, so there is a clear moment of crossing and the last third pulls the user through it. The ring is a conic gradient masked to the border, which is the cheapest exact arc available and stays correct at any size without SVG or dasharray arithmetic, and pressure is sold through compression, a deepening inset shadow and tightening letter-spacing rather than a colour change. The genuinely hard part is aborting. A hold can end in far more ways than it begins — the pointer released outside the element, a cancelled pointer, the window losing focus, a context menu — and a ring that keeps filling after the user has switched away before firing a destructive action is the failure that matters. Pointer capture covers two of those cases and the rest are handled explicitly.
Curator’s note
The ease-in curve is the entire design. Everything else is dressing on a decision the timing already made.

