ConfirmDialog
<ConfirmDialog> is the production-grade confirmation primitive — a single component that handles the trigger, the modal, async submission, the pending state, and rollback on error. Use this in 90% of cases instead of authoring AlertDialog by hand.
When to use
- Destructive verbs: delete, archive, reset.
- Publish / unpublish or any other “applies to other users” operation.
- Anything async that requires explicit confirmation + a pending state.
Don’t use ConfirmDialog for: routine save (no confirmation; just save), notifications (Sonner), inline confirms (ConfirmPopover).
Destructive
The pending state shows pendingLabel while onConfirm resolves. Click again is suppressed during pending.
<ConfirmDialog trigger={<Button variant="destructive">Delete agent</Button>} title="Delete this agent?" description="This action cannot be undone. Run history will be permanently removed." confirmLabel="Delete" variant="destructive" onConfirm={async () => { await api.delete(agent.id); }}/>Default (non-destructive)
<ConfirmDialog trigger={<Button>Publish v3</Button>} title="Publish a new version?" description="A new immutable version will be created." confirmLabel="Publish" onConfirm={async () => api.publish(agent.id)}/>API
| Prop | Type | Default | Description |
|---|---|---|---|
trigger * | ReactNode | — | Element that opens the dialog when clicked. Pass a <Button>. |
title * | string | — | Verb-led question: "Delete this agent?" |
description * | string | — | Single sentence stating the consequence. |
confirmLabel * | string | — | Verb-only label on the confirm button. Never default to "Confirm". |
cancelLabel | string | "Cancel" | Override for i18n. |
pendingLabel | string | "Processing…" | Shown on the confirm button while onConfirm pending. |
onConfirm * | () => unknown | Promise<unknown> | — | Resolved → close. Rejected → stay open and surface the error. |
variant | "default" | "destructive" | "default" | Tints the confirm button red when destructive. |
Design guidelines
✓ Do
- Use the verb that names the action: "Delete", "Publish", "Archive". Never "OK" or "Confirm".
- Make the description state the consequence in one sentence — no ambiguity.
- Let the component handle pending — never manage isLoading separately.
✗ Don't
- Use ConfirmDialog for save / cancel forms. That's the wrong gravity.
- Skip the description because the title "looks complete." Without consequence framing, users click yes.
- Catch the error inside onConfirm and resolve. Throw it; ConfirmDialog rollbacks the pending state.
Accessibility
- Inherits
AlertDialogsemantics — focus trap, Esc = cancel, focus returns to the trigger. - Cancel is keyboard-default focus.
Related
AlertDialog— Underlying primitive when you need full control.ConfirmPopover— Inline / anchored confirmation.