Hold to confirm

Free · MITReact Native · Expo

Confirms an irreversible action by holding: ink fills from the leading edge, the label inverts where the ink passes, and haptics tick at every quarter.

View as Markdown
Try it on your phone
Scan to open this demo in your phone's browser and use it with your fingers.

Installation

$ npx shadcn@latest add https://goodui.dev/native/r/hold-to-confirm.json

Then run npx expo install --fix so react-native-reanimated match your Expo SDK. Files land in components/goodui-native/hold-to-confirm.

Usage

import { HoldToConfirm } from "@/components/goodui-native/hold-to-confirm/hold-to-confirm";

<HoldToConfirm confirmedLabel="Project deleted" onConfirm={() => api.deleteProject(id)}>
  Delete project
</HoldToConfirm>

When to use

Use it for
  • Irreversible or costly actions: delete a project, send money, cancel a subscription.
  • Places where a confirmation dialog would interrupt more than it helps.
Reach for something else
  • Frequent, reversible actions: use button, and offer Undo through toast.
  • Actions that must be reachable with one quick tap.

API reference

PropTypeDefault
children*
What happens, as a verb: "Delete project".
string—
onConfirm*
Runs when the hold completes. Return a promise to show a spinner until it settles.
() => unknown—
duration
How long to hold, in ms. Default 1200.
number1200
tone
`danger` for destructive actions, `accent` for costly but positive ones such as sending money.
"danger" | "accent""danger"
confirmedLabel
Shown with the check once confirmed, for example "Deleted".
string—
hint
Hint under the hold, read by screen readers too. Same as `labels.hint`.
string—
disabled
Dims the control and ignores presses.
booleanfalse
resetAfter
Return to the idle state this many ms after confirming. `false` keeps the confirmed state.
number | false1800
labels
Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide.
Partial<HoldToConfirmLabels>—
style
Style for the outer container.
StyleProp<ViewStyle>—
testID
Identifier for end-to-end tests.
string—

Motion and haptics

  • The ink fills linearly over the duration. Letting go early drains it on the smooth spring.
  • The label exists twice, in the base and ink colors, clipped by the ink, so it inverts exactly where the ink is.
  • Haptic: select at 25, 50 and 75 percent
  • Haptic: warning at the end of the hold
  • Haptic: confirm when confirmed, error when the promise rejects
  • Progress runs on the UI thread; the early-release decision is made there so a release at the last moment never cancels a completed hold.

Accessibility

  • Screen reader users cannot hold: a double tap runs the action directly, and the hint says what happens.
  • Busy state while a returned promise is pending; the result is announced.

Notes for AI

  • Label with a verb and the object: 'Delete project', not 'Delete'.
  • Return a promise from onConfirm to show a spinner until it settles.
  • Pass resetAfter={false} when the screen navigates away after confirming.