Alert Dialog
A modal dialog that interrupts the user with important content and expects a response.
Installation
npx shadcn@latest add @redpanda/alert-dialogpnpm dlx shadcn@latest add @redpanda/alert-dialogyarn dlx shadcn@latest add @redpanda/alert-dialogbunx shadcn@latest add @redpanda/alert-dialognubx shadcn@latest add @redpanda/alert-dialogaube dlx shadcn@latest add @redpanda/alert-dialogUsage
Basic Example
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger,} from '@/components/redpanda-ui/alert-dialog';
<AlertDialog> <AlertDialogTrigger render={<Button variant="destructive">Delete Account</Button>} /> <AlertDialogContent> <AlertDialogHeader> <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle> <AlertDialogDescription> This action cannot be undone. This will permanently delete your account and remove your data from our servers. </AlertDialogDescription> </AlertDialogHeader> <AlertDialogFooter> <AlertDialogCancel>Cancel</AlertDialogCancel> <AlertDialogAction>Delete Account</AlertDialogAction> </AlertDialogFooter> </AlertDialogContent></AlertDialog>;Confirmation Dialog
<AlertDialog> <AlertDialogTrigger render={<Button>Save Changes</Button>} /> <AlertDialogContent> <AlertDialogHeader> <AlertDialogTitle>Save changes?</AlertDialogTitle> <AlertDialogDescription> This will update your profile information. You can change these settings later. </AlertDialogDescription> </AlertDialogHeader> <AlertDialogFooter> <AlertDialogCancel>Cancel</AlertDialogCancel> <AlertDialogAction>Save Changes</AlertDialogAction> </AlertDialogFooter> </AlertDialogContent></AlertDialog>Warning Dialog
<AlertDialog> <AlertDialogTrigger render={<Button variant="outline">Reset Settings</Button>} /> <AlertDialogContent> <AlertDialogHeader> <AlertDialogTitle>Reset all settings?</AlertDialogTitle> <AlertDialogDescription> This will restore all settings to their default values. Your custom configurations will be lost. This action cannot be undone. </AlertDialogDescription> </AlertDialogHeader> <AlertDialogFooter> <AlertDialogCancel>Keep Settings</AlertDialogCancel> <AlertDialogAction>Reset Settings</AlertDialogAction> </AlertDialogFooter> </AlertDialogContent></AlertDialog>Controlled State
const [open, setOpen] = useState(false)
<AlertDialog open={open} onOpenChange={setOpen}> <AlertDialogTrigger render={<Button>Open Dialog</Button>} /> <AlertDialogContent> <AlertDialogHeader> <AlertDialogTitle>Controlled Dialog</AlertDialogTitle> <AlertDialogDescription> This dialog's open state is controlled by React state. </AlertDialogDescription> </AlertDialogHeader> <AlertDialogFooter> <AlertDialogCancel onClick={() => setOpen(false)}>Cancel</AlertDialogCancel> <AlertDialogAction onClick={() => { // Perform action setOpen(false) }}> Confirm </AlertDialogAction> </AlertDialogFooter> </AlertDialogContent></AlertDialog>When to use
Use Alert Dialog for critical actions that require explicit user confirmation. This component blocks interaction with the rest of the application until resolved:
Use Alert Dialog when:
- Confirming destructive actions (delete, reset, logout)
- Warning about irreversible changes
- Requiring explicit consent for important operations
- Preventing accidental data loss
- Blocking workflow until user makes a decision
Don’t use Alert Dialog when:
- The action is easily reversible
- You need complex form inputs (use Dialog instead)
- Showing temporary notifications (use Toast instead)
- Displaying non-critical information (use Alert instead)
- The confirmation is optional or less critical
Anatomy
The Alert Dialog component is built using Base UI primitives with a layered modal structure:
Modal Layer Stack:┌─────────────────────────────────────────────────────┐│ AlertDialogPortal (React Portal) ││ ┌─────────────────────────────────────────────────┐ ││ │ AlertDialogOverlay (backdrop) │ ││ │ ┌─────────────────────────────────────────────┐ │ ││ │ │ AlertDialogContent (modal container) │ │ ││ │ │ ┌─────────────────────────────────────────┐ │ │ ││ │ │ │ AlertDialogHeader │ │ │ ││ │ │ │ ├── AlertDialogTitle │ │ │ ││ │ │ │ └── AlertDialogDescription │ │ │ ││ │ │ └─────────────────────────────────────────┘ │ │ ││ │ │ ┌─────────────────────────────────────────┐ │ │ ││ │ │ │ AlertDialogBody (optional, scrolls) │ │ │ ││ │ │ └─────────────────────────────────────────┘ │ │ ││ │ │ ┌─────────────────────────────────────────┐ │ │ ││ │ │ │ AlertDialogFooter │ │ │ ││ │ │ │ ├── AlertDialogCancel (outline button) │ │ │ ││ │ │ │ └── AlertDialogAction (primary button) │ │ │ ││ │ │ └─────────────────────────────────────────┘ │ │ ││ │ └─────────────────────────────────────────────┘ │ ││ └─────────────────────────────────────────────────┘ │└─────────────────────────────────────────────────────┘
Trigger (outside modal):AlertDialogTrigger → Opens the modalComponent Hierarchy:
- AlertDialog (Root): Manages dialog state and provides context
- AlertDialogTrigger: Button or element that opens the dialog
- AlertDialogPortal: Renders modal outside normal DOM tree
- AlertDialogOverlay: Semi-transparent backdrop (blocks interaction)
- AlertDialogContent: Main modal container with animations
- AlertDialogHeader: Contains title and description
- AlertDialogTitle: Primary heading (required for accessibility)
- AlertDialogDescription: Explanatory text
- AlertDialogBody: Optional content between the header and footer, such as a list of affected resources or a type-to-confirm input. Scrolls when the content overflows
- AlertDialogFooter: Contains action buttons
- AlertDialogCancel: Cancel/dismiss action (typically outline style)
- AlertDialogAction: Primary action button (typically destructive style)
Key Features:
- Focus Management: Automatically traps focus within dialog
- Escape Handling: Closes dialog when user presses Escape
- Click Outside: Closes dialog when clicking backdrop
- Animations: Smooth fade and scale transitions
- Accessibility: Proper ARIA attributes and screen reader support
Examples
Triggered from a Menu
Close Confirmation
Shared Across Multiple Triggers
Destructive Confirmation
Sign-out Confirmation
With a Body
Put content that isn’t the title or description — affected resources, a type-to-confirm input, an error — in an AlertDialogBody between the header and footer, as you would a DialogBody in a Dialog. The layout matches Dialog’s: header, body, and footer each carry their own padding, dividers separate the body from the header and footer, and the body scrolls while the header and footer stay pinned. AlertDialogContent has no padding of its own, so content placed directly inside it, outside these three parts, sits flush against the dialog edge.
<AlertDialogContent> <AlertDialogHeader> <AlertDialogTitle>Delete orders-v2?</AlertDialogTitle> <AlertDialogDescription>Every message in the topic is deleted.</AlertDialogDescription> </AlertDialogHeader> <AlertDialogBody>{/* affected resources, confirmation input, errors */}</AlertDialogBody> <AlertDialogFooter> <AlertDialogCancel>Cancel</AlertDialogCancel> <AlertDialogAction variant="destructive">Delete topic</AlertDialogAction> </AlertDialogFooter></AlertDialogContent>Props
Component Props
AlertDialogBody
padding?'none' | 'sm' | 'md' | 'lg'
Padding around the body content. The inner content is wrapped so scroll shadows can sit flush against the body edges — set `padding` here rather than overriding via `className`, which targets the outer scroll container.
'none' | 'sm' | 'md' | 'lg''md'spacing?'none' | 'sm' | 'md' | 'lg'
Vertical gap between direct children of the body.
'none' | 'sm' | 'md' | 'lg''md'scrollShadow?boolean
Show fading top/bottom shadows when the body overflows, cueing the user that there is more content above or below.
booleantrueKeyboard interactions
| Key | Action |
|---|---|
Enter / Space on AlertDialogTrigger |
Opens the dialog and focuses the first focusable element. |
Tab / Shift+Tab |
Cycles focus within the dialog — focus is trapped while open. |
Escape |
Triggers the Cancel action (closes without confirming). |
Enter on AlertDialogAction |
Confirms the action. |
Accessibility
AlertDialogis a specialised modal: unlikeDialog, it cannot be dismissed by clicking outside. The only exits are the explicitAlertDialogAction,AlertDialogCancel, andEscape.- Always include an
AlertDialogTitleandAlertDialogDescription— Base UI wires them toaria-labelledbyandaria-describedbyautomatically. - The dialog uses
role="alertdialog", which makes screen readers announce its contents immediately on open. - Focus is trapped while open and returned to the trigger on close. Background content is inert.
Controlled vs uncontrolled
Uncontrolled — the dialog owns open state:
<AlertDialog onOpenChange={(open) => console.log('open:', open)}> <AlertDialogTrigger>Delete</AlertDialogTrigger> <AlertDialogContent>{/* ... */}</AlertDialogContent></AlertDialog>Controlled — open the dialog programmatically (e.g. after a mutation error):
const [open, setOpen] = useState(false);
async function handleDelete() { setOpen(true); // show confirmation before firing mutation}
<AlertDialog open={open} onOpenChange={setOpen}> <AlertDialogContent> <AlertDialogHeader> <AlertDialogTitle>Delete topic?</AlertDialogTitle> <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription> </AlertDialogHeader> <AlertDialogFooter> <AlertDialogCancel>Cancel</AlertDialogCancel> <AlertDialogAction onClick={confirmDelete}>Delete</AlertDialogAction> </AlertDialogFooter> </AlertDialogContent></AlertDialog>;Upgrading to the sectioned layout
AlertDialog now lays out like Dialog. AlertDialogContent no longer pads its children (p-6, gap-4); AlertDialogHeader and AlertDialogFooter pad themselves, and AlertDialogBody holds everything between them. A dialog made of a header and a footer only needs no changes. Otherwise:
| Before | Now |
|---|---|
Content between the header and footer as a direct child of AlertDialogContent |
Wrap it in AlertDialogBody, or it sits flush against the dialog edge. |
Inputs, lists, or alerts inside AlertDialogDescription or AlertDialogHeader |
Move them into AlertDialogBody, and keep the description to text. |
A scroll wrapper (min-h-0 overflow-y-auto) around long content, or overflow-y-auto on AlertDialogContent |
Drop it: AlertDialogBody scrolls while the header and footer stay pinned. |
A full-bleed footer built from negative margins (-mx-6 -mb-6 border-t …) |
Drop the override: the footer pads itself, with a divider after a body. |
p-* or gap-* overrides on AlertDialogContent |
Set padding and spacing on AlertDialogBody instead. |