Skip to content
Component installs need the registry setup
VegaStack Design

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
stable
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-dialog

Install with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.

pnpm dlx shadcn@4.21.0 add @vegastack/alert-dialog

Run 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

AlertDialog — data-slot="alert-dialog"
AlertDialogAction — data-slot="alert-dialog-action"
AlertDialogCancel — data-slot="alert-dialog-cancel"
AlertDialogContent — data-slot="alert-dialog-content"
AlertDialogDescription — data-slot="alert-dialog-description"
AlertDialogFooter — data-slot="alert-dialog-footer"
AlertDialogHeader — data-slot="alert-dialog-header"
AlertDialogMedia — data-slot="alert-dialog-media"
AlertDialogOverlay — data-slot="alert-dialog-overlay"
AlertDialogPortal — data-slot="alert-dialog-portal"
AlertDialogTitle — data-slot="alert-dialog-title"
AlertDialogTrigger — data-slot="alert-dialog-trigger"

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
        └── AlertDialogAction

Basic

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

PropTypeDefaultDescription
size"default" | "sm"—
PropTypeDefaultDescription
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" with aria-modal, named by AlertDialogTitle through aria-labelledby and described by AlertDialogDescription through aria-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 AlertDialogCancel to 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>, so AlertDialogPortal re-applies the enclosing theme scope on a display: contents host inside the portal (OVL-13).
  • The icon in AlertDialogMedia is decorative — the title and description carry the meaning, so the destructive intent is never signalled by the tile's colour alone.
ContractStates tested
Behaviourdefault, open, closed
Accessibilitynative-or-base-ui-semantics, browser-accessibility-test, keyboard-navigation
Visualdefault, open, closed, hover, focus

Do / Don't

Do
Use an Alert Dialog when the user must answer before anything else happens, and label the action with the verb it performs — Delete, Allow, Share.
Don't
Use an Alert Dialog for anything dismissible or informational, or give it a third action; that is a Dialog.

Deviations

Upstream's file plus packages/ui/upstream/patches/alert-dialog.patch. Every hunk:

  • OVL-23 — AlertDialogOverlay sets Base UI's forceRender, 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 — AlertDialogPortal reads useInternalThemeScope() and wraps its children in a display: contents element carrying that class — the same host shape dialog uses.
  • A11Y-9 — AlertDialogContent mirrors Base UI's data-base-ui-inert markers onto native inert through useModalInert. No modal context here: Base UI's AlertDialog omits the modal prop from its root, so an alert dialog is always modal and the hook is always enabled.
  • BRD-1 — AlertDialogContent draws a real 1px border border-border instead of upstream's ring-1 ring-foreground/10 box-shadow outline. A border survives forced colours and matches Elevation; nothing else about the surface moves.
  • OVL-18 — AlertDialogFooter drops upstream's -mx-4 -mb-4 rounded-b-xl border-t bg-muted/50 p-4 band, so it is as plain as the header; the actions stay right-aligned from sm (two equal columns at size="sm"). AlertDialogCancel defaults to variant="secondary", not outline. MK, 2026-09-25.
  • LAY-17 — AlertDialogContent takes grid-cols-1 (one minmax(0, 1fr) column) in place of the grid's implicit auto column, so a no-wrap child cannot widen the popup, and wrap-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 from sm) with wrap-anywhere, with or without media. MK, 2026-10-13.
  • DOC-2 — import { cn } from "cn" becomes @vegastack/design; our npm layer owns cn.
  • DOC-1 — prettier reflow plus the registry-stamp.mjs provenance header; no decision of their own.

On this page