Search for a command to run...
A modal that overlays the page for focused tasks. Solid `surface.primary` card scales in from center over a blurred backdrop; focus traps + Esc-to-close are handled by Radix. Use for create / edit forms; for destructive confirms, reach for `AlertDialog`.
DialogContent ships with a built-in close button in the top-right (showCloseButton={false} to suppress). DialogHeader / DialogFooter arrange title + description and action rows with consistent spacing — use them as the structural slots.
This is the standard pattern for dialogs whose body can scroll (long forms, anything
that overflows on mobile). The header and footer float over the top and bottom edges with a
progressive-blur scrim, and the content scrolls underneath — so the title and actions are
always reachable and the dialog never grows past max-h.
Make DialogContent a flex column with overflow-hidden p-0; the scroll area is the only
in-flow child (no-scrollbar min-h-0 flex-1 overflow-y-auto) and carries the top/bottom
padding that clears the floating chrome. Use no-scrollbar — the blur scrim already signals
the edges, so the native track would just read as visual noise. Each edge is two stacked layers — a gradient.primary scrim plus
a progressiveBlur.top / .bottom backdrop-blur mask — followed by the DialogHeader /
DialogFooter.
Render the header markup after the scroll area in the DOM. Stacking is paint-order, not
visual order: the floating chrome is absolutely positioned, the form's relative children
(image pickers, radio cards) are not, so the chrome must come later in the DOM to paint above
them. The footer already does; the header must too — otherwise scrolled content bleeds over
the title. Don't reach for z-index (it buries the built-in close button) — fix the order.
Make the floating chrome pointer-events-none, then re-enable it on the interactive
controls (pointer-events-auto on the footer buttons). The header/footer overlay the top and
bottom of the scroll area, so without this a wheel/touch gesture started over them is swallowed
instead of scrolling the content underneath. The blur layers are already pointer-events-none;
the DialogHeader (title only) and DialogFooter shell need it too, and each button opts back
in.
DialogThe state container. Forwards every Radix Dialog.Root prop.
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Controlled open state. |
defaultOpen | boolean | false | Uncontrolled initial state. |
onOpenChange | (open: boolean) => void | — | Fires when the dialog opens or closes. |
modal | boolean | true | Trap focus + block outside interaction. Leave on unless you have a specific reason to opt out. |
DialogContentThe centered panel. Portaled with an overlay. Forwards every Radix Dialog.Content prop.
| Prop | Type | Default | Description |
|---|---|---|---|
showCloseButton | boolean | true | Render the built-in Xmark close button in the top-right. Set false to suppress. |
className | string | — | Default is sm:max-w-md. Override for narrower confirms or wider form modals. |
DialogTitle / DialogDescriptionTypography slots. DialogTitle is required for screen-reader announcement — even if you hide it with sr-only, give it a meaningful label.
DialogTitle — text-base font-medium text-fg-high.DialogDescription — text-sm text-fg-mid. Embedded <a> get underline + hover-brighten.DialogHeader / DialogFooterLayout slots. Header is a flex flex-col gap-1.5 for title-above-description; Footer is flex flex-col-reverse gap-2 sm:flex-row sm:justify-end so the primary action sits to the right on desktop and on top (via flex-reverse) on mobile.
DialogCloseWrap a button with <DialogClose asChild> to dismiss the dialog (e.g. the Cancel button in the footer).
Use Dialog for focused tasks that need the user's full attention — create / edit forms, onboarding steps, confirm flows that need context.
Always set a DialogTitle. Even decorative dialogs need one for screen readers — wrap in
sr-only if you don't want it visible.
Put the primary action on the right of DialogFooter, secondary actions to its left. The
flex-col-reverse default flips this on mobile so the primary action stays on top.
Style the Cancel / dismiss button as variant="ghost" — the primary action (solid) owns
the emphasis, so the escape hatch should recede. Same for AlertDialogCancel, Drawer, and
DetachedSheet.
Use variant="outline" for Cancel. An outlined button competes with the primary action for
attention; ghost is the correct low-weight treatment.
For any dialog whose body can scroll, use the floating header & footer pattern above — chrome floats over the edges with a progressive-blur scrim and content scrolls underneath. It keeps the title and actions pinned and stops the dialog from outgrowing the viewport on mobile.
Use Dialog for destructive confirms. AlertDialog is the right primitive — it forces a
deliberate choice and styles the destructive action.
Stack a Dialog inside another Dialog. Open one at a time — the focus-trap and Esc handling don't compose well when nested.
Use Dialog for content that's reachable any other way (a profile preview, a hint). Reach for
Popover or HoverCard so the page stays usable.