AlertDialog
Modal that interrupts to ask for an explicit answer before a consequential action, plus a ConfirmButton shorthand. Wraps Base UI AlertDialog.
Code
import { ConfirmButton } from "@fadymondy/nasaq/web";export function DeleteProject({ onDelete }: { onDelete: () => void }) { return ( <ConfirmButton title="Delete this project?" description="Issues and files are removed. This cannot be undone." confirmLabel="Delete" onConfirm={onDelete} > Delete project </ConfirmButton> );}Overlays · stable
Live examples and controls: AlertDialog in the lab.
Install
npx shadcn@latest add https://docs.nasaqui.com/r/alert-dialog.jsonA modal that stops the user to confirm something consequential: deleting a project, discarding changes, publishing to
every customer. Unlike Dialog it has no × button and an outside press does not close it, so the
only ways out are the two buttons (or Esc, which cancels). ConfirmButton is the shorthand for the common case: a
button that asks first.
When to use
- A destructive or hard-to-undo action ("Delete this project?").
- A choice that affects other people or money, where an accidental click is costly.
When not to use
- A small form or a non-critical prompt: use
Dialog. - Feedback after an action ("Deleted"): use
toast; prefer undo over confirm when the action is reversible. - Every click. Confirm dialogs used on routine actions train people to click through them.
Import
import { AlertDialog, AlertDialogAction, AlertDialogCancel, ConfirmButton } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"Quick start
import { ConfirmButton } from "@fadymondy/nasaq/web";
export function DeleteProject({ onDelete }: { onDelete: () => void }) {
return (
<ConfirmButton
title="Delete this project?"
description="Issues and files are removed. This cannot be undone."
confirmLabel="Delete"
onConfirm={onDelete}
>
Delete project
</ConfirmButton>
);
}Anatomy
AlertDialog Base UI AlertDialog.Root (no DOM)
├─ AlertDialogTrigger Base UI AlertDialog.Trigger (use render={<Button />})
└─ AlertDialogContent Portal + backdrop + popup data-slot="alert-dialog-content"
├─ AlertDialogBackdrop data-slot="alert-dialog-backdrop" (rendered for you)
├─ AlertDialogHeader data-slot="alert-dialog-header"
│ ├─ AlertDialogTitle data-slot="alert-dialog-title"
│ └─ AlertDialogDescription data-slot="alert-dialog-description"
└─ AlertDialogFooter data-slot="alert-dialog-footer"
├─ AlertDialogCancel data-slot="alert-dialog-cancel"
└─ AlertDialogAction data-slot="alert-dialog-action"API
AlertDialog, AlertDialogTrigger, AlertDialogClose
Aliases of Base UI AlertDialog.Root, .Trigger, .Close. Common AlertDialog props:
| Prop | Type | Default | Description |
|---|---|---|---|
open? | boolean | none | Controlled open state. |
defaultOpen? | boolean | false | Initial state when uncontrolled. |
onOpenChange? | (open: boolean, details) => void | none | Called when it opens or closes. |
Base UI fixes modal to true and disables outside-press dismissal.
AlertDialogContent
Base UI AlertDialog.Popup props. The popup is max-w-md, centred, and scrolls if taller than the viewport. It has no close (×) button.
AlertDialogAction, AlertDialogCancel
AlertDialogActionProps extends Base UI AlertDialog.Close props. Both close the dialog when pressed.
| Prop | Type | Default | Description |
|---|---|---|---|
variant? | "primary" | "danger" | "secondary" | "ghost" | "danger" (action), "ghost" (cancel) | Button look, from buttonVariants. |
onClick? | MouseEventHandler | none | Run the confirmed work here. |
AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription
Same shape as the Dialog parts. The footer is a reversed column on mobile and an end-aligned row from sm.
ConfirmButton
ConfirmButtonProps extends Button props (minus title, onClick, children), so size, disabled and className apply to the trigger.
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | required | The trigger button's label. |
title | ReactNode | required | The question. Name what is affected. |
description? | ReactNode | none | What happens and whether it can be undone. |
confirmLabel? | ReactNode | the trigger label if it is a string, else "Confirm" / "تأكيد" | Label of the confirming button. |
cancelLabel? | ReactNode | "Cancel" / "إلغاء" | Label of the cancel button. |
variant? | ButtonProps["variant"] | "danger" | Look of the trigger and the confirm button. |
onConfirm | () => void | Promise<unknown> | required | Runs on confirm. A promise keeps the dialog open with a loading confirm button; it closes on resolve and stays open on reject. |
Examples
Composed by hand
import {
AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription,
AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, Button,
} from "@fadymondy/nasaq/web";
export function DiscardChanges({ onDiscard }: { onDiscard: () => void }) {
return (
<AlertDialog>
<AlertDialogTrigger render={<Button variant="secondary" />}>Discard</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Discard unsaved changes?</AlertDialogTitle>
<AlertDialogDescription>Your edits to this page will be lost.</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Keep editing</AlertDialogCancel>
<AlertDialogAction onClick={onDiscard}>Discard</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}Async confirm, Arabic copy
import { ConfirmButton } from "@fadymondy/nasaq/web";
export function PublishAr({ publish }: { publish: () => Promise<void> }) {
return (
<ConfirmButton
variant="primary"
title="نشر الإصدار؟"
description="سيظهر الإصدار لجميع العملاء."
confirmLabel="نشر"
cancelLabel="إلغاء"
onConfirm={publish}
>
نشر الإصدار
</ConfirmButton>
);
}Accessibility
Provided by Base UI AlertDialog: role="alertdialog", modal, focus is trapped, page content is inert, and focus
returns to the trigger on close.
| Key | Action |
|---|---|
Esc | Cancels and closes. |
Tab / Shift+Tab | Cycles focus inside the dialog. |
Enter / Space | Activates the focused button. |
- Always render
AlertDialogTitleandAlertDialogDescription; they name and describe the dialog. - Put
AlertDialogCancelfirst in the footer so it takes initial focus andEnternever confirms by accident. - Localise
title,description,confirmLabelandcancelLabelyourself; only the default cancel/confirm labels are built in.
RTL & i18n
- Header text uses
text-start; the footer aligns withsm:justify-end, so both followdir. - Built-in strings (
ConfirmButtononly): "Cancel" / "إلغاء" and "Confirm" / "تأكيد", chosen when the Nasaq locale starts withar.
Styling & tokens
- Surface level 3:
bg-popover,text-popover-foreground,border-border,rounded-floating. Backdropbg-nq-fg/15(light),bg-nq-bg/60(dark). - Motion: opacity only, 150ms, via
data-starting-style/data-ending-style. - Target
[data-slot=alert-dialog-content],-backdrop,-header,-footer,-title,-description,-action,-cancel. Extend withclassName.
Do / Don't
- Do phrase the title as the question and name what is affected; make the action label a verb ("Delete"), not "OK".
- Do use
variant="danger"for destructive actions. - Don't put forms or long content in it; use
Dialog. - Don't confirm reversible actions; offer undo in a
toast.
Related
Lab
https://docs.nasaqui.com/?path=/docs/components-overlays-alert-dialog--docs
SetupWizard
Multi-step first-run wizard with a server-decided completion gate, plus a guided connect step that waits live for an agent to enroll.
ConfirmProvider
One app-wide confirm dialog you can await from any handler, so `if (!(await confirm({ title }))) return;` replaces window.confirm with a styled, localised AlertDialog.