Skip to content

Components · Overlays

ConfirmDialog

Confirmation for consequential actions: danger tone, type-to-confirm, reason, async pending and error.

@rookcom/monarch-reactComponent

Type to confirm, with consequences

Usage examples

Required reason

Error and retry

From a menu with useConfirm

Purpose

Asks the person to confirm an action before it happens, says plainly what will change, and stays open while the action runs so a failure can be shown and retried. It replaces window.confirm and window.prompt, which cannot be themed, translated or show progress.

Use when

  • Deleting, revoking, erasing or anything else that cannot be undone.
  • Actions with wide effects (archive a workspace, remove a member) where a reason may be recorded.
  • Irreversible actions that deserve a typed confirmation (the record’s name).

Avoid when

  • Reversible actions: do them and offer Undo in a toast instead.
  • Collecting more than a short reason: use a Dialog with a form.

Design principles

  • Say what happens, not "Are you sure?": the title asks the question, the description and consequences say what changes.
  • Name the button after the action ("Delete project"), never "OK" or "Yes".
  • Safe by default: focus starts on Cancel, the least destructive action; outside clicks do not dismiss it; Enter in a field never confirms a mismatched phrase.
  • Honest progress: while the action runs nothing dismisses the dialog, and a failure is shown in place with the reason and a chance to retry.
  • The danger tone is reserved for destructive actions: a red confirm button and a calm warning icon, nothing more.

API reference

import { ConfirmDialog, useConfirm } from '@rookcom/monarch-react';
ExportKindRenderingRoot class
ConfirmDialogcomponentclient.mn-confirm-dialog
useConfirmhookclient–

ConfirmDialog is controlled (open + onOpenChange) or uncontrolled (trigger, defaultOpen). It forwards its ref to the dialog element.

onConfirm(details) runs the action. Return a promise for the pending state; reject (or throw) to show an error and keep the dialog open.

useConfirm() returns { confirm, element }: render element once and await confirm(options), which resolves with { reason? } or null when cancelled.

Other DialogContent props (className, onEscapeKeyDown, data-*, …) pass through to the dialog.

Generated from the TypeScript declarations of packages/react/src/components/overlays/confirm-dialog.

Properties

reason is { label, description?, placeholder?, required?, maxLength?, requiredMessage? }; the trimmed text arrives as details.reason.

pending and error can be controlled when the product tracks the mutation itself.

Every visible string is a prop (confirmLabel, cancelLabel, confirmPhraseLabel, confirmPhraseMismatch, errorMessage, reason.requiredMessage) for translation.

ConfirmDialog

Confirmation for consequential actions, built on Dialog with `role="alertdialog"`. Supports a danger tone, a consequence list, type-to-confirm, an optional or required reason, and async actions with pending and error states. Focus starts on Cancel, the least destructive action, and nothing dismisses the dialog while the action runs.

PropTypeDescription
onConfirm required(details: ConfirmDetails) => unknownRuns the action. Return a promise to keep the dialog open in a pending state until it settles: on success it closes (see `closeOnConfirm`), on rejection the error is shown inside the dialog and the person can retry.
title requiredReactNodeThe question, e.g. "Delete this project?". Becomes the accessible name.
asChildboolean
cancelLabelReactNodeDefault "Cancel".
childrenReactNodeExtra content between the description and the fields, e.g. a step-up slot.
classNamestring
closeLabelstring | nullAccessible label of the built-in close button. Pass null to hide the button.
closeOnConfirmbooleanClose after `onConfirm` succeeds. Default `true`.
closeOnOutsideClickbooleanWhether a click outside the dialog closes it. Default `true`; turn it off for forms with typed values so a stray click does not lose them. Escape and the close button still work.
confirmLabelReactNodeLabel of the confirming button. Name the action ("Delete project"), not "OK". Default "Confirm".
confirmPhrasestringType-to-confirm: the confirm button stays disabled until this exact phrase (case-sensitive, surrounding spaces ignored) is typed.
confirmPhraseLabelReactNodeLabel of the phrase field. Default: Type "<phrase>" to confirm.
confirmPhraseMismatchReactNodeShown when the person submits a phrase that does not match.
consequencesReactNode[]Concrete consequences, shown as a list ("12 tasks will be deleted").
defaultOpenbooleanUncontrolled initial open state.
descriptionReactNodeWhat will happen. Becomes the accessible description.
errorReactNodeControlled error message, shown above the actions.
errorMessage((error: unknown) => ReactNode)Turns a rejected `onConfirm` into a message. Default: the Error's message, or a generic line.
escapeGuardbooleanIgnore Escape while focus is on a control whose own popup is open (a combobox or menu button with `aria-expanded="true"`), so that press only closes the popup. Default `true`.
fullScreenOnPhonebooleanBelow 640px wide, fill the whole screen instead of floating with a margin.
idstring
initialFocus"cancel" | "field"Where focus goes on open. `cancel` (default) is the least destructive action; `field` focuses the phrase or reason field when there is one.
onCancel(() => void)Called when the dialog is dismissed without confirming.
onOpenChange((open: boolean) => void)
openbooleanControlled open state.
pendingbooleanControlled pending state, for actions the product tracks itself.
reasonConfirmReasonFieldAsk for a reason (optional or required); it is passed to `onConfirm`.
size"sm" | "md" | "lg" | "xl"Dialog width. Default `sm`.
tone"danger" | "default"`danger` for destructive or irreversible actions: red confirm button and a warning icon.
triggerReactElement<unknown, string | import("/home/runner/work/monarch/monarch/node_modules/@types/react/index").JSXElementConstructor<any>>The element that opens the dialog (rendered with `asChild`), e.g. a Button.

Variants

PropValuesDescription
tone'default''danger'danger uses the danger Button and a warning icon in the header. Exposed as data-tone.
confirmPhrase'undefined''string'Type-to-confirm. The confirm button is disabled until the phrase matches exactly (case-sensitive; surrounding spaces ignored).
reason'undefined''optional''required'A labelled textarea; required keeps the confirm button disabled until it has text.
size'sm''md''lg''xl'Dialog width; default sm.

States

StateExposed asBehaviour
Blockeddisabled (confirm button)The phrase does not match or a required reason is empty.
Mismatcharia-invalid (field)Pressing Enter with a wrong phrase, or confirming without a required reason, shows a field error.
Pendingaria-busy (dialog and confirm button), data-busyThe confirm button shows a spinner and keeps focus; Cancel is disabled; Escape and outside clicks are ignored; fields are read-only.
Errorrole="alert"A danger Alert above the actions, also linked to the confirm button with aria-describedby. The person can retry or cancel.

Interaction behaviour

  • Opening focuses Cancel (initialFocus="cancel"), or the phrase or reason field with initialFocus="field".
  • Confirming calls onConfirm. A synchronous action closes the dialog at once; a promise keeps it pending until it settles.
  • On success it closes (unless closeOnConfirm={false}); on failure it stays open with the error and the confirm button ready to retry.
  • Escape and Cancel call onCancel and close it, except while pending.
  • Every opening starts clean: typed phrase, reason and old errors are cleared.
  • From a DropdownMenu item, call confirm() from useConfirm in onSelect; focus returns to the menu trigger afterwards.

Keyboard support

KeysAction
Tab / Shift+TabMoves between the fields, Cancel and the confirm button; focus stays inside.
Enter (phrase field)Confirms when the phrase matches; otherwise shows the mismatch error.
Enter / Space (button)Activates Cancel or the confirm button.
EscapeCancels and returns focus to the trigger; ignored while pending.

Accessibility

Role
alertdialog (aria-modal)
Accessible name
title; description becomes the accessible description.
  • Initial focus is on the least destructive action (APG alert dialog pattern).
  • The phrase field is labelled with the exact phrase and has autocomplete, autocorrect and spellcheck off.
  • Field errors are announced politely and linked to the field; an action error is announced assertively (role="alert").
  • The pending confirm button keeps focus and sets aria-busy, so keyboard users do not lose their place.
  • The warning icon is decorative (aria-hidden); the title carries the meaning.

Responsive behaviour

  • Default width 24rem (sm), never wider than the screen minus 2rem.
  • The footer wraps its buttons on narrow screens; long phrases wrap inside the label.
  • Long consequence lists scroll inside the body while the actions stay visible.

Theme support

  • Dialog surface tokens, danger tokens for the icon and button, and the muted surface behind the phrase.
  • Light, dark and more-contrast themes apply automatically; motion follows prefers-reduced-motion.

Do and don't

Do

  • Describe the concrete result, with counts, in description or consequences.
  • Use confirmPhrase with the record’s own name for irreversible, high-impact actions.
  • Return the mutation’s promise from onConfirm so progress and failure show in the dialog.
  • Throw errors with a message the person can act on, or map them with errorMessage.

Don't

  • Do not confirm reversible actions; offer Undo instead.
  • Do not use "OK" / "Cancel" or a vague "Are you sure?" title.
  • Do not use the danger tone for non-destructive actions.
  • Do not close the dialog yourself before the action has finished.
  • Dialog: The base it is built on, for custom content.
  • DropdownMenu: Destructive menu items that open a confirmation.
  • Toast: Undo for reversible actions instead of a confirmation.
  • Alert: Shows the error inside the dialog.

Dependencies

KindDepends on
npm packagesreact
Monarch componentsButton, Alert, Field, Input, Dialog

Testing coverage

  • Opens as an alertdialog with focus on Cancel; axe passes; Escape cancels (onCancel) and restores focus.
  • Keeps focus inside and ignores outside clicks.
  • Confirms a synchronous action and closes.
  • Type-to-confirm: blocked until the exact phrase; Enter with a mismatch marks the field invalid and describes the error; surrounding spaces are ignored.
  • Async: pending sets aria-busy, keeps focus on the confirm button, disables Cancel and blocks Escape; success closes.
  • Async: a rejection shows the error, links it to the confirm button, keeps it open and allows retry.
  • Generic message for non-Error rejections; errorMessage maps errors; a synchronous throw is shown too.
  • Required reason blocks until filled and is passed trimmed; an empty optional reason is omitted.
  • Consequences render as a list; children come before the fields.
  • Controlled pending and error; controlled open with closeOnConfirm={false}.
  • Starts clean on every opening.
  • useConfirm resolves with the details on confirm and null on cancel, and runs async work inside the dialog first.
  • useConfirm from a DropdownMenu item: focus lands on Cancel, and returns to the menu trigger after closing.
  • Renders on the server (closed and open).
CheckCovered
Axe in jsdomYes
Server renderingYes
Browser suitesVisual baselines: gallery/confirm-dialog and overlays/confirm-dialog-light (open); Cross-browser axe: /components/confirm-dialog

Tests: packages/react/src/components/overlays/confirm-dialog