Confirm Dialog React component
A focused alert dialog for confirming an action, with default/warning/danger variants, an async-aware confirm button with a spinner and inline error, and a safety-first default focus.
About the Confirm Dialog component
Confirm Dialog is a small dialog that asks for confirmation before an action. It shows an icon, a title, an optional description and Cancel and Confirm buttons over a blurred backdrop. variant switches between default, warning and danger, which changes the icon and the color of the confirm button.
onConfirm can return a promise. While it runs, the confirm button shows a spinner and both buttons are disabled; the dialog closes when it resolves, and if it fails the dialog stays open with the error message. It uses role="alertdialog" with aria-modal, keeps Tab inside the dialog, locks page scrolling, closes on Escape and on a backdrop click (both can be turned off) and gives focus back to where it was when it closes.
When to use it
Use it before destructive or hard-to-undo actions: deleting a project, removing a team member, cancelling a subscription, discarding unsaved changes or publishing something.
For forms, longer content or more than two choices, use Modal. For a message that does not block the page after an action, use Alert Toast.
Installation
Add the Confirm Dialog component to your project with the shadcn CLI. It lands in components/confirm-dialog.tsx as plain React + Tailwind CSS source you own and can edit.
npx shadcn@latest add @reactframe/confirm-dialogOr copy the source by hand and install its dependencies:
npm install motion clsx tailwind-mergeProps
<ConfirmDialog /> accepts 16 props, all optional.
| Prop | Type | Default |
|---|---|---|
| open | boolean | — |
| defaultOpen | boolean | false |
| onOpenChange | (open: boolean) => void | — |
| title | React.ReactNode | "Are you sure?" |
| description | React.ReactNode | — |
| variant | "default" | "warning" | "danger" | "default" |
| icon | React.ReactNode | — |
| confirmLabel | string | "Confirm" |
| cancelLabel | string | "Cancel" |
| onConfirm Can return a promise; the dialog shows a spinner and disables both buttons until it settles, then closes on success and stays open (with the thrown message) on failure. | () => void | Promise<void> | — |
| onCancel | () => void | — |
| closeOnBackdrop | boolean | true |
- openboolean
- defaultOpenbooleanDefault: false
- onOpenChange(open: boolean) => void
- titleReact.ReactNodeDefault: "Are you sure?"
- descriptionReact.ReactNode
- variant"default" | "warning" | "danger"Default: "default"
- iconReact.ReactNode
- confirmLabelstringDefault: "Confirm"
- cancelLabelstringDefault: "Cancel"
- onConfirm
Can return a promise; the dialog shows a spinner and disables both buttons until it settles, then closes on success and stays open (with the thrown message) on failure.
() => void | Promise<void> - onCancel() => void
- closeOnBackdropbooleanDefault: true
Show all 16 propsShow fewer
| closeOnEscape | boolean | true |
| theme | "dark" | "light" | "dark" |
| contained | boolean | false |
| className | string | — |
- closeOnEscapebooleanDefault: true
- theme"dark" | "light"Default: "dark"
- containedbooleanDefault: false
- classNamestring
npm dependencies
Installed for you by the shadcn CLI.
Confirm Dialog FAQ
- How do I show a loading state while the action runs?
- Return a promise from onConfirm, for example from an async function that calls your API. The spinner and the disabled buttons are handled for you, and the dialog closes once the promise resolves.
- What happens if the action fails?
- If onConfirm throws or its promise rejects, the dialog stays open and shows the error's message in red. If what was thrown is not an Error, it shows a generic "Something went wrong" message. The message clears the next time the dialog opens.
- Which button is focused when it opens?
- Confirm for the default variant. Cancel for warning and danger, so pressing Enter by accident does not run a destructive action.
- Can I stop it closing on Escape or a backdrop click?
- Set closeOnEscape={false} or closeOnBackdrop={false}. While onConfirm is running, neither one closes it.
- How do I show it inside a box instead of over the whole page?
- Set contained. It then renders in place with position: absolute instead of in a portal and does not lock page scrolling, so give the parent position: relative.