Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelog
  1. Home
  2. Blog
  3. Mobile UI in React
  4. React Slide to Confirm: When It Beats a Confirm Dialog
Auf Deutsch lesen
  1. Blog
  2. Mobile UI in React
  3. React Slide to Confirm: When It Beats a Confirm Dialog
Mobile UI in React
Mobile UI in React

React Slide to Confirm: When It Beats a Confirm Dialog

SR
Serban Rusu · Founder of Wingo UI
Oct 13, 2026 · 8 min read

On this page

0%
  1. When should you use a slide to confirm instead of a dialog?
  2. Which props matter on the Slide To Confirm?
  3. How do you keep a slide to confirm accessible?
  4. How does it behave on a phone?
  5. Which slide to confirm pitfalls should you avoid?
  6. What should you install next?
  7. FAQ
    1. Is slide to confirm better than a confirm dialog?
    2. Is a slide to confirm accessible?
    3. How far should the thumb travel before it confirms?
    4. How do I build slide to unlock in React?
    5. Is the Wingo UI Slide To Confirm free?

TL;DR

Use a React slide to confirm when one irreversible action is the whole point of a phone screen and an accidental tap is likely, such as paying, confirming a delivery or clocking in. Use a confirm dialog when the action is rare, sits next to other actions or needs to explain consequences, and use undo when the action can be reversed. A slide is only accessible with a real button as the thumb, Enter and screen reader activation that confirm without dragging, and a live region for loading, success and error.

Published Oct 13, 2026

A React slide to confirm is the control you see in delivery and payment apps: a track with a thumb that you drag to the end before anything happens. It looks like a gimmick until you watch someone tap Pay with a phone in one hand on a moving bus. This guide covers when a slide beats a confirm dialog for irreversible actions, the props that matter on the Wingo UI Slide To Confirm, and how to keep it usable for keyboard and screen reader users.

When should you use a slide to confirm instead of a dialog?

When one irreversible action is the reason the screen exists, and the person is on a phone. A confirm dialog adds a second tap in a different place, which stops accidents but also adds a step every single time. A slide turns the confirmation into the action itself: one deliberate gesture, and a stray tap does nothing. Apple uses the same idea for an action nobody should trigger from a pocket: its iPhone guide tells you to drag the Power Off slider (opens in a new tab) to turn the phone off.

Here is how we pick between the four ways to guard an action:

PatternBest forCostWingo UI
Undo in a toastreversible actions: archive, delete to trash, remove from a listnone up front, the action runs at onceToast (free)
Inline confirmsmall deletes in a list or toolbar, mostly on desktopa second click in the same spotDelete Button (free)
Confirm dialograre, irreversible actions that need a sentence of consequencesa modal, two taps, focus movesAlert Dialog (Pro)
Slide to confirmthe one hero action on a phone screen: pay, confirm delivery, clock in, send moneya drag, or Enter on a keyboardSlide To Confirm (Pro)

Two rules settle most cases. If the action can be undone, offer undo and skip the confirmation; our React swipe to delete tutorial shows that pattern. If the person needs to read something before deciding ("Your 14 listings and every conversation will be deleted"), use a dialog, because a slider label truncates on a narrow track and cannot hold a warning. Never stack a slide and a dialog for the same action. The slide already is the confirmation.

Drag the thumb to the end to confirm the delivery. Let go early and it springs home; on a desktop, Tab to the thumb and press Enter.
$ npx wingo-ui@latest add slide-to-confirm
ProSlide To Confirm docs

Which props matter on the Slide To Confirm?

Most screens need label, onConfirm and the status labels. The rest tune how hard the slide is and how it looks.

  • onConfirm runs when the thumb reaches the end or on Enter. Return a promise: the thumb parks with a spinner, then a check pops in and the fill covers the track when it resolves, or the track shakes, the thumb goes home and the error text shows for 2 seconds when it rejects. A synchronous throw counts as a rejection, and it never runs twice for one slide.
  • threshold (default 0.9) is the share of the travel past which a release confirms. That is deliberately harder than other swipes in the library, which complete at a quarter of their size. A flick only counts past 60%. For comparison, Coinbase's React Native SlideButton (opens in a new tab) defaults its checkThreshold to 0.7 (checked October 2026).
  • resetAfter (default 0, never) slides back to idle after success. Keep it at 0 for a delivery that stays done, and set it for repeatable actions like clocking in.
  • status and onStatusChange make it controlled for server-driven flows: set "idle" to reset, or "success" to show it confirmed without calling onConfirm.
  • labels holds the status texts: loading ("Processing…"), success ("Confirmed"), error ("Something went wrong. Try again.") and hint ("Drag to the end, or press Enter"), which is the thumb's screen reader description.
  • variant, tone, size and radius cover the look. variant="solid" is for the one hero action, tone is neutral by default (black in light mode, white in dark), and the track is 48, 56 or 64px tall, a comfortable touch target at every size.
  • haptics (default on) ticks when the thumb arms and plays success or error patterns on phones that support vibration.

Here is a delivery confirmation for a driver app. The request throws on a failed response, because fetch only rejects on network errors, and a 500 that resolves would show a success.

tsx
"use client";
import { PackageCheck } from "lucide-react";
import { SlideToConfirm } from "@/components/ui/slide-to-confirm";
type ConfirmDeliveryProps = { orderId: string; onDelivered: () => void };
export function ConfirmDelivery({ orderId, onDelivered }: ConfirmDeliveryProps) {
return (
<SlideToConfirm
className="max-w-sm"
tone="success"
label="Slide to confirm delivery"
icon={<PackageCheck />}
labels={{ loading: "Confirming…", success: "Delivered", error: "Could not confirm. Try again." }}
onConfirm={async () => {
const res = await fetch(`/api/orders/${orderId}/delivered`, { method: "POST" });
// fetch resolves on a 500; throw so the control shows the error and the thumb goes home
if (!res.ok) throw new Error(`Request failed with ${res.status}`);
}}
onStatusChange={(status) => {
if (status === "success") onDelivered();
}}
/>
);
}

The same control is your slide to unlock React component: pass label="Slide to unlock", an unlock icon and resetAfter={1500}, and unlock in onConfirm. Swipe to confirm is the same pattern under another name.

How do you keep a slide to confirm accessible?

Make the thumb a real button and give it a path that never drags. A slide to confirm button built from a div with pointer handlers fails both. A slide is a dragging movement, and WCAG 2.2 success criterion 2.5.7 (opens in a new tab) (level AA) asks for a single-pointer alternative. Apple's gestures guidelines (opens in a new tab) say the same in plain words: "Don't assume that people can use a specific gesture to perform a given task." This is what the Wingo UI component does, and what to check in any other:

  • The thumb is a <button>. Its accessible name is the label ("Slide to confirm delivery"), it switches to the status text while loading and after success, and aria-describedby points at the hint: "Drag to the end, or press Enter".
  • The keyboard confirms without dragging. Tab to the thumb, then Enter, Space or End glide it to the end and confirm. Left and Right preview the travel in 10% steps and spring back after a moment, so arrow keys alone never confirm by accident. Home returns it.
  • Screen readers confirm with one activation. The component treats a click that arrives without a pointer press, such as a double tap in VoiceOver or TalkBack, as a confirm. A mouse click or a finger tap on the thumb only hops it forward 16px to teach the gesture.
  • Status is announced. A role="status" live region reads loading, success and error, so a blind user hears "Delivered" instead of silence.
  • Disabled stays findable. disabled dims the control and blocks dragging and keys, but the thumb stays focusable with aria-disabled, so a screen reader still finds it. Say why it is disabled in the text next to it.
  • Reduced motion is respected. The idle shimmer across the label turns off, and so does the 16px hop.

For a dialog, the Alert Dialog handles the equivalent: focus lands on Cancel, the safe choice, Escape cancels, a click on the dimmed page does nothing, and focus returns to the trigger when it closes.

tsx
import { Trash2 } from "lucide-react";
import { AlertDialog } from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
export function DeleteProject({ remove }: { remove: () => Promise<void> }) {
return (
<AlertDialog
trigger={
<Button variant="soft" tone="danger" leftIcon={<Trash2 />}>
Delete project
</Button>
}
tone="danger"
title="Delete this project?"
description="Its 3 environments and every deployment will be deleted for good."
confirmLabel="Delete project"
confirmLoadingText="Deleting"
onConfirm={remove}
/>
);
}

When onConfirm returns a promise, the action shows a spinner, Cancel and Escape wait, and a failure shakes the card and shows the error above the buttons, ready to retry.

Press Delete to open the dialog. Focus starts on Cancel; confirm to see the loading state, or press Escape to back out.
$ npx wingo-ui@latest add alert-dialog
ProAlert Dialog docs

How does it behave on a phone?

It scrolls, arms and confirms like a native control, as long as it sits outside other swipeable areas. A vertical swipe that starts on the track still scrolls the page, because the drag locks to one axis once it starts and the track sets touch-pan-y (our Tailwind touch manipulation guide explains that value). Past the threshold the thumb grows a little, the fill deepens and the phone ticks; release there and it glides to the end. A tap on the track hops the thumb forward to show which way it goes. In right-to-left layouts the thumb starts on the right and the icon mirrors.

The tick is a bonus. As of October 2026, caniuse (opens in a new tab) shows no navigator.vibrate in Safari, so iPhone users rely on the visual armed state. Our Web Vibration API guide covers the gaps.

Which slide to confirm pitfalls should you avoid?

  • A slide for everyday actions. Save, Send message or Add to cart do not need a guard, and a slide on them is friction. Keep it for the action that costs money or cannot be taken back.
  • A slide inside a horizontal swiper. In a carousel or swipeable tabs the two drags compete. Put the slide outside the swiping area, or pin it in a sticky bottom bar.
  • A label that does not fit. The label truncates on narrow tracks. "Slide to pay" fits anywhere; a sentence does not. Put the amount and the details above the control.
  • Trusting the client alone. The component never runs onConfirm twice for one slide, but a flaky network can still retry a request. Make the endpoint idempotent, for example with an idempotency key per order.
  • A track on a desktop-only form. A slide works with a mouse and a keyboard, but on a desktop admin screen a button with a dialog is what people expect.

For the full mobile layer, from bottom sheets to safe areas, read our guide to mobile-first React components or browse Mobile UI in React.

What should you install next?

npx wingo-ui@latest add slide-to-confirm copies the component with the Spinner and the motion, haptics and state helpers it imports, and installs motion and lucide-react. Slide To Confirm is part of Wingo UI Pro, so sign in first with npx wingo-ui@latest login. For small deletes, the free Delete Button is one command away: npx wingo-ui@latest add delete-button.

Components in this post

  • Slide To Confirm

    A React slide to confirm control: drag the thumb to the end to approve a payment or risky action.

    Pro
  • Alert Dialog

    A React alert dialog for confirmations: an async confirm that loads, fails inline and shakes, plus an await confirm() API.

    Pro
  • Delete Button

    A React delete button with inline confirmation: the bin opens a small confirm and cancel panel in place, no dialog needed.

    Free

FAQ

Is slide to confirm better than a confirm dialog?

For one irreversible action on a phone screen, often yes: it takes one deliberate gesture instead of two taps, and it is hard to trigger by accident. For rare actions on desktop, or actions that need an explanation, a confirm dialog is clearer.

Is a slide to confirm accessible?

Only if it has a non-drag path. WCAG 2.2 success criterion 2.5.7 Dragging Movements asks for a single-pointer alternative, so the thumb should be a real button that confirms on Enter, Space or a screen reader activation, with a description that says so and a live region that announces the result.

How far should the thumb travel before it confirms?

Far enough that a stray swipe does not count. The Wingo UI Slide To Confirm confirms on release past 90% of the track by default, or on a flick past 60%; Coinbase's React Native SlideButton defaults to 70% (as of October 2026).

How do I build slide to unlock in React?

Slide to unlock is the same control with a different label: a track, a draggable thumb on a motion value, a threshold near the end, and a reset after success. With Wingo UI, pass label="Slide to unlock" and resetAfter to SlideToConfirm.

Is the Wingo UI Slide To Confirm free?

No. Slide To Confirm and Alert Dialog are Pro components. The Delete Button with its inline confirm is free.

  • Mobile UI
  • Confirmation
  • Accessibility
  • React
  • Gestures

Share

SR

About the author

Serban Rusu

Founder of Wingo UI

Serban Rusu founded Wingo UI. He builds its component library, CLI and MCP server, and writes about React interfaces for phones and AI coding agents.

More from Serban
Keep reading

Related posts

All posts
Mobile UI in React

React Action Sheet: iOS-Style, Async and Responsive

Build a React action sheet like iOS: red destructive rows, a separate Cancel, an awaitable show() that waits for async actions, and a dialog from 768px.

SRSerban Rusu·Oct 16, 2026·9 min read
Mobile UI in React

React Swipe to Delete: iOS Swipe Actions With Undo

Build React swipe to delete on list rows: iOS-style swipe actions, a full swipe that deletes, undo through a toast, and buttons for mouse and keyboard users.

SRSerban Rusu·Oct 13, 2026·9 min read
Build it: screens and apps

React Settings Page: Section Nav, Save Bar, Danger Zone

Build a React settings page with a section nav on desktop, an iOS-style push list on phones, a save bar for unsaved changes and a typed danger zone.

SRSerban Rusu·Oct 16, 2026·11 min read
Newsletter

Get new posts by email

New guides, tutorials and comparisons from the Wingo UI blog, sent when they are published.

No spam. Unsubscribe at any time.

WingoUI

Animated, configurable, mobile-first React components and blocks. Copy the source, make it yours, and let your coding agent build with it.

Product

  • Components
  • Templates
  • Pricing
  • AI agents
  • Theme generator

Resources

  • Docs
  • Blog
  • Changelog
  • Changelog RSS
  • llms.txt

Legal

  • Terms
  • Privacy
  • Refunds
  • License
  • Imprint

Component categories

  • Buttons & Actions
  • Inputs
  • Forms
  • Navigation
  • Overlays
  • Feedback
  • Data Display
  • Tables & Lists
  • Charts & Stats
  • Layout
  • Media
  • AI Kit
  • Text & Effects
  • Mobile
  • Commerce
  • Marketing Sections
  • Blocks
  • Hooks & Utilities

© 2026 Wingo UI

Wingo UI