Confirm
The "are you sure" moment, in one piece. A Dialog that behaves as an alert, a cancel and an action button, an optional type-to-confirm guard, and a hook that turns it into a promise.
Installation
pnpm dlx nachui add confirmAnatomy
With only a trigger as child the panel lays itself out: icon if you gave one, title, description, the text guard when
requireText is set, then Cancel and the action in the footer. The panel has role="alertdialog", so screen readers announce it as something that needs an answer, and Escape and the overlay count as cancel.Pass a
Confirm.Content to rearrange the footer or add something of yours between the description and the buttons.Composition
A hybrid is visible composition. Open
confirm.tsx and these are the elements it is made of, in this order:Variants
variant="destructive" paints the action button with the destructive tokens and moves the initial focus to Cancel, so a stray Enter does not delete anything. The default variant focuses the action instead, because there the fast path is the right one.Async confirm
Return a promise from
onConfirm and the panel waits for it. The action shows the spinner, both buttons disable, Escape and the overlay stop closing it. It closes when the promise resolves and stays open, buttons back on, when it rejects, so the reader can try again or bail out.Type to confirm
requireText adds an input under the description and keeps the action disabled until what the reader typed matches exactly. Enter inside the input confirms once it matches.The hook
Wrap the app, or the part of it that asks questions, in
ConfirmProvider. Then useConfirm() gives you a function that opens the panel and resolves true on confirm and false on cancel, Escape or the overlay. Calls queue up, one panel at a time.Three drafts in the archive.
API Reference
Confirm
| Prop | Type | Default | Description |
|---|---|---|---|
title | ReactNode | - | The question, also the accessible name |
description | ReactNode | - | What happens if they say yes |
icon | ReactNode | - | Rendered in a circle above the title |
variant | 'default' | 'destructive' | 'default' | Tone of the action and where the focus lands |
confirmText | string | 'Confirm' | Label of the action button |
cancelText | string | 'Cancel' | Label of the cancel button |
requireText | string | - | Text the reader must type before confirming |
labels | Partial<{ confirm; cancel; typeToConfirm }> | English | Every visible string, typeToConfirm is a function |
open | boolean | - | Controlled open state |
defaultOpen | boolean | false | Open state when uncontrolled |
onOpenChange | (open: boolean) => void | - | Called when the open state changes |
onConfirm | () => void | Promise<void> | - | Called by the action; a promise keeps it pending |
onCancel | () => void | - | Called by cancel, Escape and the overlay |
className | string | - | Classes for the default panel |
children | ReactNode | - | A Confirm.Trigger and, optionally, a Confirm.Content |
Confirm.Trigger
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Use the child element as the trigger |
className | string | - | Additional CSS classes |
Confirm.Content
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | Cancel and Action | Replaces the footer; header and guard stay |
className | string | - | Additional CSS classes |
Confirm.Cancel
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | cancelText | Label |
className | string | - | Additional CSS classes |
... | - | - | Any Button prop except variant |
Confirm.Action
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | confirmText | Label |
className | string | - | Additional CSS classes |
... | - | - | Any Button prop except variant |
useConfirm
| Signature | Description |
|---|---|
useConfirm(): (options: ConfirmOptions) => Promise<boolean> | Opens a panel, resolves true on confirm and false otherwise |
ConfirmOptions takes title, description, icon, variant, confirmText, cancelText, requireText and labels, with the same meaning as the props above. It needs a ConfirmProvider above it.Found something to improve?
Notice a bug, typo, or missing detail on this page? Help us make the documentation better by opening a GitHub issue.