AlertDialog
Blocking confirmation dialog for destructive or critical actions. Built on Base UI's alert-dialog primitive with role="alertdialog", focus trap, and explicit-action-only dismissal — the user must choose Confirm or Cancel.
Playground
Installation
pnpm add @tessinaui/uiUsage
import {
AlertDialog,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogBody,
AlertDialogFooter,
AlertDialogAction,
AlertDialogCancel,
} from "@tessinaui/ui";<AlertDialog intent="error">
<AlertDialogTrigger render={<Button intent="error">Delete</Button>} />
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete this project?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Delete</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>Showcase
When to use AlertDialog vs. Modal vs. Alert
| Use AlertDialog when… | Use Modal when… | Use Alert when… |
|---|---|---|
| The user must explicitly confirm or cancel | The dialog holds form fields, lists, or rich content | You only need to inform the user inline |
| The action is destructive, irreversible, or critical | Outside-click and Escape are acceptable dismissals | The message can be ignored without consequence |
You need stronger accessibility semantics (role="alertdialog") | The decision isn't critical | The notification appears in the page flow, not as an overlay |
Anatomy
<AlertDialog>
<AlertDialogTrigger>…</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle />
<AlertDialogDescription />
</AlertDialogHeader>
<AlertDialogBody /> {/* optional */}
<AlertDialogFooter>
<AlertDialogCancel />
<AlertDialogAction />
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>AlertDialogHeader automatically renders the icon for the current intent (or your custom icon). AlertDialogAction is intent-aware — it inherits the dialog's intent so destructive confirmations are coloured correctly without extra props.
Size
| Size | Max width |
|---|---|
"sm" | max-w-sm (24rem) |
"md" (default) | max-w-md (28rem) |
"lg" | max-w-lg (32rem) |
"xl" | max-w-xl (36rem) |
Intent
intent drives the header icon (auto) and the default colour of AlertDialogAction. The popup border stays neutral by default — opt in to a coloured border with showIntentBorder when the surrounding context doesn't already convey urgency.
| Value | Header icon | Default action button | Border with showIntentBorder |
|---|---|---|---|
"none" (default) | Info | bg-primary | border-border |
"error" | CircleX | bg-error | border-error |
"warning" | TriangleAlert | bg-warning | border-warning |
"success" | CircleCheck | bg-success | border-success |
"info" | Info | bg-info | border-info |
Rounded
| Value | CSS |
|---|---|
"none" | rounded-none |
"sm" | rounded-lg |
"md" (default) | rounded-xl |
"lg" | rounded-2xl |
"full" | rounded-3xl |
Footer layout
AlertDialogFooter accepts layout:
"row"(default) — horizontal, end-aligned. Standard desktop pattern."stacked"— full-width buttons stacked vertically. iOS / mobile style.
<AlertDialogFooter layout="stacked">
<AlertDialogAction>Power off</AlertDialogAction>
<AlertDialogCancel>Cancel</AlertDialogCancel>
</AlertDialogFooter>When layout="stacked", place AlertDialogAction first — flex-col-reverse puts it on top so the destructive action sits closest to the title.
Dismissal behaviour
Unlike Modal, the alert dialog cannot be dismissed by:
- Clicking outside the popup
- Pressing Escape
The user must choose AlertDialogAction, AlertDialogCancel, or your own AlertDialogClose. This is the W3C-recommended pattern for role="alertdialog".
Controlled
const [open, setOpen] = React.useState(false);
<AlertDialog open={open} onOpenChange={setOpen}>
…
</AlertDialog>Custom action via AlertDialogClose
When you need full control over the button (e.g., async confirmation, loading state), use AlertDialogClose directly with render:
<AlertDialogClose
render={
<Button onClick={async () => {
await deleteProject();
// dialog closes automatically after the click handler resolves
}}>
Delete forever
</Button>
}
/>Accessibility
- Renders with
role="alertdialog"so screen readers treat it as a critical interruption. AlertDialogTitleis wired toaria-labelledby,AlertDialogDescriptiontoaria-describedby.- Focus traps inside the popup. On open, focus moves to the first focusable element (typically
AlertDialogCancel). - On close, focus returns to the trigger.
- The popup is not dismissible by outside click or Escape — both gestures are intentionally swallowed.
- Use
intent="error"for destructive confirmations; the semantic colour, header icon, and action button intent all update so assistive tech and sighted users get the same signal.
RTL
Pass dir="rtl" to the root. The header icon, title, footer button order, and stacked layout all follow the reading direction.
<AlertDialog dir="rtl" intent="error">
<AlertDialogTrigger render={<Button intent="error">حذف</Button>} />
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>هل أنت متأكد من الحذف؟</AlertDialogTitle>
<AlertDialogDescription>لا يمكن التراجع عن هذا الإجراء.</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>إلغاء</AlertDialogCancel>
<AlertDialogAction>حذف</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>API Reference
AlertDialog (root)
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "md" | "lg" | "xl" | "md" | Max-width of the popup |
rounded | "none" | "sm" | "md" | "lg" | "full" | "md" | Corner radius — also inherited by AlertDialogAction and AlertDialogCancel |
intent | "none" | "error" | "warning" | "success" | "info" | "none" | Header icon and default action-button intent |
showIntentBorder | boolean | false | When true, the popup border picks up the intent colour. Opt-in — neutral border by default |
dir | "ltr" | "rtl" | "ltr" | Reading direction |
open | boolean | — | Controlled open state |
defaultOpen | boolean | false | Uncontrolled initial state |
onOpenChange | (open, event) => void | — | Fired when open state changes |
AlertDialogContent
Inherits size, rounded, intent, and dir from the root. Override any of them to scope the popup independently.
AlertDialogHeader
| Prop | Type | Default | Description |
|---|---|---|---|
icon | ReactNode | intent icon | Custom leading icon next to the title |
showIcon | boolean | true | Render an icon — set to false for icon-less alerts |
AlertDialogFooter
| Prop | Type | Default | Description |
|---|---|---|---|
layout | "row" | "stacked" | "row" | Horizontal end-aligned, or full-width stacked vertically |
AlertDialogAction, AlertDialogCancel
Both render a Button. Pass any Button prop (size, rounded, loading, leadingIcon, etc.). Both auto-close the dialog on click.
AlertDialogActiondefaults tovariant="primary"and inherits the dialog'sintent.AlertDialogCanceldefaults tovariant="outline"andintent="none".
AlertDialogTrigger, AlertDialogClose
Render a <button>. Use render={<CustomElement />} to swap in a Button or any element. Standard Base UI render-prop pattern.
Sub-components
AlertDialogTitle, AlertDialogDescription, AlertDialogBody, AlertDialogBackdrop — all accept className and any HTML props.
Notes
- Built on
@base-ui/react/alert-dialog— the same primitive used by Radix and shadcn for the same component, with W3Crole="alertdialog"semantics. - The popup uses Base UI's
data-[starting-style]anddata-[ending-style]attributes for entry/exit animation. - The header icon background tints automatically per intent (
bg-error-lightetc.) for a soft callout.
Modal
An accessible dialog overlay built on Base UI. Compound component pattern with Header, Body, Footer, Title, Description, and Close. Five sizes. Five corner-rounding options. LTR and RTL support.
Drawer
An accessible slide-in panel built on Base UI Dialog. Compound component pattern with Header, Body, Footer, Title, and Description. Four sides (right, left, top, bottom). Five sizes. Five corner-rounding options. Auto-stacking footer for sheets. LTR and RTL support.