Alert dialog
Stricter sibling of Dialog. Same visual anatomy (Header + Body + Footer with borders, Yuno tokens), but Radix enforces role='alertdialog' + hardened dismissal: no overlay-click close, no ESC by default, focus trap can't leak. Use ONLY for truly irreversible actions where an accidental dismiss must not close the flow. For 99% of confirmations, use Dialog instead.
Anatomy
Five parts, mirroring Dialog. Trigger and Content, plus three composable regions inside Content: Header (bordered bottom edge, title + optional description), Body (required — the 'Are you sure...' copy and consequence), Footer (Cancel + Action). Note: AlertDialog has NO close X in the corner — the user must click Cancel or Action to close.
- 1AlertDialogTrigger
Any focusable element that opens the dialog. Pass asChild so it wraps YOUR Button instead of adding its own.
- 2AlertDialogOverlay
Fixed dimmer over the app: bg-foreground/24, no blur. Applied automatically by AlertDialogContent — click on it does NOT close (that's the whole point of AlertDialog).
- 3AlertDialogContent
The centered card: bg-background, border, rounded-lg (10px), shadow-lg (composite). sm:max-w-md (~448px), can override className up to max-w-7xl. Traps focus. No close X.
- 4AlertDialogHeader
Top row with bottom border. Ships flex flex-col gap-1.5 border-b p-6 text-left. Contains AlertDialogTitle + optional AlertDialogDescription.
- 5AlertDialogBody
Required in the Yuno convention (parity with Dialog). Ships flex-1 overflow-auto border-b p-6. Owns the divider ABOVE the Footer. Every AlertDialog has a Body — the 'Are you sure...' question and the consequence go here, not stuffed into the Header.
- 6AlertDialogFooter
Bottom row WITHOUT its own border. Ships flex flex-col-reverse gap-2 p-6 sm:flex-row sm:justify-end. Contains AlertDialogCancel + AlertDialogAction (both required). Cancel is outline (Radix pre-styled), Action is Primary.
- 7AlertDialogCancel
Explicit Cancel button. Radix ships it as buttonVariants({ variant: 'outline' }). Required — an AlertDialog without Cancel breaks the 'user must choose' contract.
- 8AlertDialogAction
The confirm/destructive button. Radix ships it as buttonVariants() → Primary (Yuno indigo). NEVER override to variant='destructive' — Yuno reserves red for real errors.
- 9AlertDialogTitle
Required by Radix for accessibility. text-lg font-semibold leading-none. Announces via role='alertdialog' for screen readers.
- 10AlertDialogDescription
Optional. text-sm text-muted-foreground. Same 99% title-only rule as Dialog — most AlertDialogs skip this and put the substance in AlertDialogBody.
Variants
Same three regions as Dialog (Header + Body + Footer) — different content per region.
<AlertDialog>
<AlertDialogTrigger asChild>
<Button>Delete workspace</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete workspace</AlertDialogTitle>
</AlertDialogHeader>
<AlertDialogBody>
<p className="text-sm text-foreground/85">
Are you sure you want to delete this workspace? All rules, lists
and API keys will be permanently erased and this action cannot
be undone.
</p>
</AlertDialogBody>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Delete workspace</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog><AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Discard changes?</AlertDialogTitle>
</AlertDialogHeader>
<AlertDialogBody>
<p className="text-sm text-foreground/85">
You have unsaved changes to this rule. If you leave now, they'll be
lost.
</p>
</AlertDialogBody>
<AlertDialogFooter>
<AlertDialogCancel>Keep editing</AlertDialogCancel>
<AlertDialogAction>Discard changes</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent><AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Sign out</AlertDialogTitle>
</AlertDialogHeader>
<AlertDialogBody>
<p className="text-sm text-foreground/85">
You have unsaved changes. Signing out now will discard them.
</p>
</AlertDialogBody>
<AlertDialogFooter>
<AlertDialogCancel>Stay signed in</AlertDialogCancel>
<AlertDialogAction>Sign out</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>Width
Default sm:max-w-md (~448px, same as Dialog). AlertDialogs almost never need to be wider — the pattern is a short, focused decision. If you're reaching for max-w-2xl+, question whether you should be using a Sheet or full-screen page instead.
States
Radix drives open/closed via data-state. Content fades in with the animate-dialog-in animation (Yuno keyframe). No zoom animation (unlike Dialog) — the stricter contract feels more definitive with a plain fade. Overlay fades in with the same timing.
Motion
AlertDialog appears with a plain fade — no zoom. The contract is 'you must decide', and the motion reflects that: definitive, no bounce.
| Enter animation | animate-dialog-in (Yuno keyframe, ~200ms fade + subtle scale) |
|---|---|
| Exit animation | reverse of enter |
| Overlay | animate-fade-in / fade-out |
| Overlay tint | bg-foreground/24 (Yuno dimmer, no blur) |
| Dismissal | Button click only — no overlay-click, no ESC |
| Radius | rounded-lg (10px) |
| Shadow | shadow-lg (composite 2-layer) |
Recipes
Ready-to-copy compositions covering the most common Yuno usages of this atom.
The default Yuno pattern for irreversible destructive actions. Title states the action ('Delete workspace'). Body carries the 'Are you sure' question + the consequence in 1-2 sentences. Footer: Cancel (outline) + Action (Primary, repeats the destructive verb).
<AlertDialog>
<AlertDialogTrigger asChild>
<Button>Revoke API key</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Revoke API key</AlertDialogTitle>
</AlertDialogHeader>
<AlertDialogBody>
<p className="text-sm text-foreground/85">
Any integrations using this key will stop working immediately.
This action cannot be undone.
</p>
</AlertDialogBody>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Revoke key</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>When the consequence needs more explanation (identifier, service impact, cross-dependencies). Body carries 2-3 sentences. If it grows past that, escalate to a Sheet — AlertDialog isn't a Sheet.
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete customer acct_9f2b1</AlertDialogTitle>
</AlertDialogHeader>
<AlertDialogBody>
<p className="text-sm text-foreground/85">
This will erase 12 payment methods, 3 subscriptions and the full
transaction history for this customer. Webhooks tied to this account
will fail on next delivery. This cannot be undone.
</p>
</AlertDialogBody>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Delete customer</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>Responsive
Import
The full compound. AlertDialogBody and AlertDialogDescription are optional in the source but AlertDialogBody is required by Yuno convention (same as Dialog).
import {
AlertDialog,
AlertDialogAction,
AlertDialogBody,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";Props
Everything else from Radix AlertDialog is forwarded via ...props. Refer to Radix's AlertDialog docs for the low-level primitive props.
| Prop | Type | Default | Description |
|---|---|---|---|
| AlertDialog.open / onOpenChange | boolean / (open: boolean) => void | — | Controlled open state. Omit both for uncontrolled — Radix handles it. |
| AlertDialogTrigger.asChild | boolean | false | Merges Radix's trigger props onto the child element instead of rendering a button. |
| AlertDialogContent.className | string | — | Merged onto the centered card. Use sparingly — AlertDialog is small on purpose. If you need width, question whether you should be using a Sheet. |
| AlertDialogHeader | component | — | Bordered top row. Ships flex flex-col gap-1.5 border-b p-6 text-left. Contains AlertDialogTitle + optional AlertDialogDescription. |
| AlertDialogBody | component | — | Required by Yuno convention. Ships flex-1 overflow-auto border-b p-6. Owns the divider ABOVE the Footer. The 'Are you sure' question and the consequence go here — never crammed into the Header. |
| AlertDialogFooter | component | — | Bottom row without its own border. Ships flex flex-col-reverse gap-2 p-6 sm:flex-row sm:justify-end. Contains AlertDialogCancel + AlertDialogAction. On mobile the primary rises to the top of the stack. |
| AlertDialogTitle | component | — | Required by Radix for accessibility. text-lg font-semibold leading-none. Announced via role='alertdialog'. |
| AlertDialogDescription | component | — | Optional and rare. text-sm text-muted-foreground. The Dashboard convention is title-only — put the substance in AlertDialogBody. |
| AlertDialogAction | component | — | The confirm button. Ships buttonVariants() → Primary. NEVER override to variant='destructive' — Yuno reserves red for real errors. |
| AlertDialogCancel | component | — | The cancel button. Ships buttonVariants({ variant: 'outline' }). Required — an AlertDialog without Cancel breaks the 'user must choose' contract. |
When to use
- Truly irreversible actions (delete workspace, revoke org-wide API key, wipe a customer's account).
- Sign out flows that would lose unsaved work.
- Discard-changes prompts where an accidental click-outside would erase user input.
- Cases where role='alertdialog' matters for screen reader urgency (compliance-sensitive prompts).
When not to use
- Routine deletes (delete a rule, delete a list, delete a route) — use Dialog. Those are reversible from your recycle bin / audit log.
- Quick create forms — use Dialog.
- Long-form edits — use Sheet.
- Anything the user could safely dismiss by clicking outside — use Dialog.
Usage
- Always include AlertDialogBody with the 'Are you sure...' question + consequence — parity with Dialog convention.
- Copy the destructive verb into the Action button ('Delete workspace', not 'Yes').
- Use supportive Cancel copy when relevant ('Stay signed in', 'Keep editing').
- Keep it short — 1 title + 1-2 sentence Body + 2 buttons. If it grows, escalate to Sheet.
- Don't skip AlertDialogBody. Every AlertDialog has a Body — even confirmations put the question in the Body, never the Header.
- Don't use AlertDialog for routine confirmations — use Dialog. AlertDialog is for irreversible actions.
- Don't use variant='destructive' on AlertDialogAction — Yuno reserves red for real errors.
- Don't stack AlertDialogs. And no nesting inside a Sheet — if the flow needs both, redesign it.
- Don't add a close X — AlertDialog intentionally has none. Cancel and Action are the only exits.
Related
Cross-links to atoms and patterns you may reach for next.
- DialogThe 99% choice. Same visual anatomy, dismissable with ESC / overlay click — reach for Dialog first for any confirmation.
- SheetFor long-form editing or explanations that need more room than any modal. Sheet is 'work here'; AlertDialog is 'decide here'.
- ToastTransient feedback after an action has run. Never a substitute for a confirmation before the action.
- ButtonAlertDialogAction and AlertDialogCancel inherit buttonVariants — the outline/primary tokens live there.
- Overlays & full-screenThe decision guide — read the AlertDialog section to know when to escalate from Dialog.