Alert Dialog
A modal that interrupts for a decision — an optional media slot, two sizes, and a footer carrying a cancel and an action.
- Status
- Since
0.1.0- Accessibility pattern
- APG alert dialog (modal)
Last updated
Install
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Alert Dialog before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify alert-dialogInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/alert-dialogRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
The same command installs the registry items it composes: @vegastack/button, @vegastack/use-modal-inert.
Usage
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
<AlertDialog>
<AlertDialogTrigger render={<Button variant="outline" />}>
Show Dialog
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete your account
from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Continue</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>;Anatomy
Examples
Composition
AlertDialogContent renders the portal and the backdrop itself. AlertDialogMedia is optional and
sits first in the header; the footer carries exactly two controls, in that order.
AlertDialog
├── AlertDialogTrigger
└── AlertDialogContent
├── AlertDialogHeader
│ ├── AlertDialogMedia
│ ├── AlertDialogTitle
│ └── AlertDialogDescription
└── AlertDialogFooter
├── AlertDialogCancel
└── AlertDialogActionBasic
A title, a description, and the cancel/continue pair. AlertDialogCancel is a Button with the
secondary variant that also closes the dialog; AlertDialogAction is a plain Button, so the
caller decides what it does and whether it closes.
Small
size="sm" on AlertDialogContent keeps the popup at max-w-xs on every breakpoint, centres the
header at every width, and lays the footer out as two equal columns instead of a right-aligned row.
Media
AlertDialogMedia is a 40px rounded tile for an icon. At the default size it moves into its own
column beside the title from the sm breakpoint up; the header's grid rows change to make room for
it, driven by has-data-[slot=alert-dialog-media] rather than by a prop.
Small with Media
At size="sm" the media tile stays above the title and the whole header stays centred, because the
column layout is gated on the default size.
Destructive
AlertDialogAction forwards every Button prop, so a destructive confirmation is
variant="destructive" on the action — never a colour written onto the footer. Tint the media tile
to match and keep the cancel control visually quieter.
RTL
The header's text-start and the media tile's column are logical, and the centring translate
carries an explicit rtl: counterpart, so a right-to-left document mirrors the surface without a
second layout. Wrap the subtree in DirectionProvider so Base UI agrees with the document's dir,
and hand dir to AlertDialogContent — the popup portals to <body>, so a dir on an ancestor of
the trigger never reaches it.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
size | "default" | "sm" | — |
| Prop | Type | Default | Description |
|---|---|---|---|
size | "default" | "icon" | "icon-lg" | "icon-sm" | "icon-xs" | "lg" | "sm" | "xs" | null | — | |
variant | "default" | "destructive" | "ghost" | "link" | "outline" | "secondary" | null | — |
AlertDialog, AlertDialogTrigger, AlertDialogPortal, AlertDialogOverlay,
AlertDialogHeader, AlertDialogMedia, AlertDialogTitle and AlertDialogDescription add no
props of their own. AlertDialogAction is a Button and accepts every Button prop, variant
and size included.
The root takes no modal prop: Base UI's AlertDialog omits it, so an alert dialog is always modal.
Accessibility
- Renders Base UI's AlertDialog: the popup is
role="alertdialog"witharia-modal, named byAlertDialogTitlethrougharia-labelledbyand described byAlertDialogDescriptionthrougharia-describedby. Focus moves into the popup and returns to the trigger on close. - Dismissal is deliberate: clicking the backdrop does not close an alert dialog. Esc
still does, and it is treated as a cancel, so give it a real
AlertDialogCancelto land on. - The background is made natively inert while the popup is open (A11Y-9), not merely focus-trapped; the hook restores each element's previous value once the last modal releases it.
- The popup portals to
<body>, soAlertDialogPortalre-applies the enclosing theme scope on adisplay: contentshost inside the portal (OVL-13). - The icon in
AlertDialogMediais decorative — the title and description carry the meaning, so the destructive intent is never signalled by the tile's colour alone.
| Contract | States tested |
|---|---|
| Behaviour | default, open, closed |
| Accessibility | native-or-base-ui-semantics, browser-accessibility-test, keyboard-navigation |
| Visual | default, open, closed, hover, focus |
Do / Don't
Deviations
Upstream's file plus packages/ui/upstream/patches/alert-dialog.patch. Every hunk:
- OVL-23 —
AlertDialogOverlaysets Base UI'sforceRender, so an alert dialog opened from inside a dialog or a sheet dims and blurs the page too. Operator decision D31 (Regent), 2026-10-30. - OVL-13 —
AlertDialogPortalreadsuseInternalThemeScope()and wraps its children in adisplay: contentselement carrying that class — the same host shapedialoguses. - A11Y-9 —
AlertDialogContentmirrors Base UI'sdata-base-ui-inertmarkers onto nativeinertthroughuseModalInert. Nomodalcontext here: Base UI's AlertDialog omits themodalprop from its root, so an alert dialog is always modal and the hook is always enabled. - BRD-1 —
AlertDialogContentdraws a real 1pxborder border-borderinstead of upstream'sring-1 ring-foreground/10box-shadow outline. A border survives forced colours and matches Elevation; nothing else about the surface moves. - OVL-18 —
AlertDialogFooterdrops upstream's-mx-4 -mb-4 rounded-b-xl border-t bg-muted/50 p-4band, so it is as plain as the header; the actions stay right-aligned fromsm(two equal columns atsize="sm").AlertDialogCanceldefaults tovariant="secondary", notoutline. MK, 2026-09-25. - LAY-17 —
AlertDialogContenttakesgrid-cols-1(oneminmax(0, 1fr)column) in place of the grid's implicitautocolumn, so a no-wrap child cannot widen the popup, andwrap-break-word, so a long unbroken word — a file name in "Delete …?" — breaks inside it.AlertDialogHeader's text tracks shrink too (minmax(0, 1fr), beside the media fromsm) withwrap-anywhere, with or without media. MK, 2026-10-13. - DOC-2 —
import { cn } from "cn"becomes@vegastack/design; our npm layer ownscn. - DOC-1 — prettier reflow plus the
registry-stamp.mjsprovenance header; no decision of their own.