Overlays

Confirm Dialog

Small alert dialog to confirm or cancel a consequential action. Async confirms show a busy button until they finish.

Installation

Install the package (React 19):

terminal
npm install @krackedui/motion

Import the styles once, in your app entry or root layout:

/src/main.tsx
import '@krackedui/motion/styles.css';

Use it anywhere inside your app's <Theme> (see Installation):

/src/ConfirmDialogExample.tsx
import { useState } from 'react';
import { Button, ConfirmDialog } from '@krackedui/motion';

export function DeletePages() {
  const [count, setCount] = useState(3);
  return (
    <ConfirmDialog
      trigger={<Button variant="danger">Delete {count} pages…</Button>}
      title={`Delete ${count} pages?`}
      description="They move to Trash for 30 days."
      confirmLabel="Delete"
      destructive
      // Returning a Promise keeps the dialog open with a busy button until it settles.
      onConfirm={() => new Promise<void>((resolve) => setTimeout(() => { setCount((c) => Math.max(0, c - 1)); resolve(); }, 1200))}
    />
  );
}

Examples

Destructive, async

Controlled, non-destructive

Static body

Delete this page?
It moves to Trash for 30 days.

API Reference

ConfirmDialog

PropTypeDefault
title
ReactNode
description
ReactNode
confirmLabel
ReactNode
"Delete" | "Confirm"
cancelLabel
ReactNode
"Cancel"
destructive
boolean
true
onConfirm
() => void | Promise<void>
onCancel
() => void
trigger
ReactNode
open
boolean
defaultOpen
boolean
false
onOpenChange
(open: boolean) => void

ConfirmDialogBody

The 300px body on its own (inside Dialog.Content or DialogSurface).

PropTypeDefault
title
ReactNode
description
ReactNode
confirmLabel
ReactNode
cancelLabel
ReactNode
"Cancel"
destructive
boolean
true
onConfirm
() => void
onCancel
() => void
loading
boolean
false
radix
boolean
false

Accessibility

  • Follows the WAI-ARIA alertdialog pattern: role="alertdialog", named by the title, described by the description.
  • Focus starts on Cancel, so pressing Enter right away never confirms by accident; focus is trapped inside.
  • Escape and Cancel dismiss it; focus returns to the trigger.
  • While an async onConfirm runs, the confirm button has aria-busy="true" and the dialog cannot be dismissed. A rejected Promise keeps it open so the user can retry.
  • Opens and closes with the shared motion layer (styles/motion.css); with prefers-reduced-motion: reduce it only fades.

Usage

  • Use before destructive or irreversible actions (delete, revoke, publish).
  • Write the confirm label as the action ("Delete"), not "OK" or "Yes".
  • Don't confirm routine, undoable actions: do them and offer Undo in a toast instead.
© 2026 Motion. Internal documentation.