Components

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.

Updated Jul 20, 2026 by Leonardo Posada
For 99% of cases, use Dialog
AlertDialog and Dialog look identical in Yuno. The only difference is stricter dismissal semantics (no ESC / overlay click) and role='alertdialog' for screen readers. Reach for AlertDialog ONLY when the action is genuinely irreversible AND an accidental dismiss would be harmful — sign out with unsaved changes, delete a workspace, revoke an org-wide API key. Every other confirmation ('Delete rule?', 'Discard changes?') uses Dialog.
Not sure this is the right surface?
Dialog, Sheet, Drawer and full-screen views compete for similar jobs. Read the decision guide to pick the right one for your case. Overlays & full-screen guide.

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.

  1. 1
    AlertDialogTrigger

    Any focusable element that opens the dialog. Pass asChild so it wraps YOUR Button instead of adding its own.

  2. 2
    AlertDialogOverlay

    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).

  3. 3
    AlertDialogContent

    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.

  4. 4
    AlertDialogHeader

    Top row with bottom border. Ships flex flex-col gap-1.5 border-b p-6 text-left. Contains AlertDialogTitle + optional AlertDialogDescription.

  5. 5
    AlertDialogBody

    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.

  6. 6
    AlertDialogFooter

    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.

  7. 7
    AlertDialogCancel

    Explicit Cancel button. Radix ships it as buttonVariants({ variant: 'outline' }). Required — an AlertDialog without Cancel breaks the 'user must choose' contract.

  8. 8
    AlertDialogAction

    The confirm/destructive button. Radix ships it as buttonVariants() → Primary (Yuno indigo). NEVER override to variant='destructive' — Yuno reserves red for real errors.

  9. 9
    AlertDialogTitle

    Required by Radix for accessibility. text-lg font-semibold leading-none. Announces via role='alertdialog' for screen readers.

  10. 10
    AlertDialogDescription

    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.

Destructive confirmation (canonical)
<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>
Discard unsaved changes
<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>
Sign out with unsaved work
<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 animationanimate-dialog-in (Yuno keyframe, ~200ms fade + subtle scale)
Exit animationreverse of enter
Overlayanimate-fade-in / fade-out
Overlay tintbg-foreground/24 (Yuno dimmer, no blur)
DismissalButton click only — no overlay-click, no ESC
Radiusrounded-lg (10px)
Shadowshadow-lg (composite 2-layer)
Every AlertDialog has a Body
Header + Body + Footer, always — same rule as Dialog. The 'Are you sure you want to delete this workspace?' question and the consequence ('All rules, lists and API keys will be permanently erased. This cannot be undone.') go in the Body. Never crammed into the Header.
Title-only is the 99% pattern
Just like Dialog, the Dashboard convention is title-only in the header. AlertDialogDescription exists but rarely helps — the substance belongs in the AlertDialogBody where there's room to breathe. Prefer a clear title ('Delete workspace') + a Body paragraph with the details.
Never used at Yuno: variant='destructive' Button
AlertDialogAction ships as Primary via buttonVariants(). Do NOT override to variant='destructive' — Yuno reserves red for actual system errors. The 'stricter dismissal' semantics of AlertDialog already carry the weight; the button stays Primary. Weight comes from copy ('Delete workspace') and the stricter modal contract, not from color.

Recipes

Ready-to-copy compositions covering the most common Yuno usages of this atom.

Destructive confirmation (canonical)

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>
Destructive with impact detail

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

Alert Dialog

Import

The full compound. AlertDialogBody and AlertDialogDescription are optional in the source but AlertDialogBody is required by Yuno convention (same as Dialog).

The full compound. AlertDialogBody is required by Yuno convention (parity with Dialog); AlertDialogDescription is rare (title-only is the 99% pattern).
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.

PropTypeDefaultDescription
AlertDialog.open / onOpenChangeboolean / (open: boolean) => voidControlled open state. Omit both for uncontrolled — Radix handles it.
AlertDialogTrigger.asChildbooleanfalseMerges Radix's trigger props onto the child element instead of rendering a button.
AlertDialogContent.classNamestringMerged onto the centered card. Use sparingly — AlertDialog is small on purpose. If you need width, question whether you should be using a Sheet.
AlertDialogHeadercomponentBordered top row. Ships flex flex-col gap-1.5 border-b p-6 text-left. Contains AlertDialogTitle + optional AlertDialogDescription.
AlertDialogBodycomponentRequired 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.
AlertDialogFootercomponentBottom 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.
AlertDialogTitlecomponentRequired by Radix for accessibility. text-lg font-semibold leading-none. Announced via role='alertdialog'.
AlertDialogDescriptioncomponentOptional and rare. text-sm text-muted-foreground. The Dashboard convention is title-only — put the substance in AlertDialogBody.
AlertDialogActioncomponentThe confirm button. Ships buttonVariants() → Primary. NEVER override to variant='destructive' — Yuno reserves red for real errors.
AlertDialogCancelcomponentThe 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

Do
  • 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
  • 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.