Dialog
A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.
Made by imskyleenInstallation
When to use
Use this decision tree to determine when to use the Dialog component:
Usage
<Dialog>
<DialogTrigger>Open Dialog</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Title</DialogTitle>
<DialogDescription>Description</DialogDescription>
</DialogHeader>
<p>Dialog Content</p>
<DialogFooter>
<button>Close</button>
</DialogFooter>
</DialogContent>
</Dialog>Props
Examples
Default
Delete
No Header
Skip the header when the dialog body stands on its own — the close button stays at the top-right and the top border is omitted since there's nothing to separate from.
No Footer
Omit the footer when the dialog is informational and doesn't require explicit actions beyond dismissing.
Scrolling content
DialogContent caps at max-h-[85vh] and DialogBody is the scrollable region — long content gets a pinned header/footer with an internal scrollbar for free. When the body overflows, fading top/bottom shadows appear automatically to signal "more above/below"; opt out with <DialogBody scrollShadow={false}>.
Fixed height
Pass height="sm" | "md" | "lg" | "xl" on DialogContent to lock the dialog to a stable pixel height (clamped to 85vh). This is useful for multi-view dialogs — wizards, step flows, gallery → detail — where the container shouldn't reflow as the user navigates between views.
Animated auto height
In the default height="auto" mode, the dialog smoothly animates between natural heights when its content changes — toggling sections, async-loaded data, expanding details. No prop opt-in needed; it's automatic for auto-height dialogs and respects prefers-reduced-motion.
No Header or Footer
Use body-only dialogs sparingly — without a visible header the dialog still needs an accessible name, so render a DialogTitle with className="sr-only" for screen readers. Add right-padding to the body so content clears the absolute-positioned close button.
Footer with helper text
When the footer pairs metadata or status text (e.g. "Changes are saved automatically") with a single dismiss action, use justify="between" so the text sits on the left and the button on the right. The footer centers items vertically by default, so the text baseline aligns cleanly with the button.
Component Props
DialogContent
Prop
Type
DialogBody
Prop
Type
Keyboard interactions
| Key | Action |
|---|---|
Enter / Space on DialogTrigger | Opens the dialog and moves focus to the first focusable element inside DialogContent. |
Tab / Shift+Tab | Cycles focus within the dialog — focus is trapped while open. |
Escape | Closes the dialog and restores focus to the trigger. |
Click outside DialogContent | Closes the dialog (modal scrim dismiss). |
Accessibility
- Render a
DialogTitle(or a visually-hidden one) — Base UI enforces this with a development warning. The dialog is labelled viaaria-labelledbypointing at the title. - Add a
DialogDescriptionwhen the dialog's purpose isn't obvious from the title; it is wired up viaaria-describedby. - Focus is trapped inside the popup while open and returned to the trigger on close. Background content is marked
aria-hiddenand made inert so assistive tech doesn't reach it. onOpenChangeis the Radix-style single-argument(open: boolean) => void; the compat layer narrows Base UI's(open, details)signature so existing handlers keep working.
Controlled vs uncontrolled
Uncontrolled — let the dialog own open state, react via onOpenChange:
<Dialog onOpenChange={(open) => console.log('open:', open)}>
<DialogTrigger>Open</DialogTrigger>
<DialogContent>{/* ... */}</DialogContent>
</Dialog>Controlled — drive open from parent state, e.g. to open the dialog from a menu item or remote action:
const [open, setOpen] = useState(false);
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger>Open</DialogTrigger>
<DialogContent>
<DialogFooter>
<Button onClick={() => setOpen(false)}>Done</Button>
</DialogFooter>
</DialogContent>
</Dialog>Credits
- We use Base UI for the dialog component.
- We take our inspiration from Shadcn UI for the dialog style.
- We use Animate UI from imskyleen for all the animations.
Recent changes
- patchv1.2.0Pin shipped dependency floors to the version we develop against. Registry items now declare ranges like `^5.1.9` (the actual installed version) instead of collapsing to `^5.0.0`, so consumers start on the known-tested baseline while caret semantics still allow any compatible release within the same major.#133
- minorv1.1.0Theme docs refresh, readability pass on semantic foregrounds, and consumer-facing Base UI regression fixes.#121
- minorv1.1.0Bulk install experience and Dialog polish.#119
- minorv1.0.0Post-Base-UI polish. Public API unchanged.#116
- majorv1.0.0Migrate every Radix-based primitive to `@base-ui/react@^1.4.0` (Base UI).#114