Dialog
A window overlaid on either the primary window or another dialog window, rendering the content underneath inert. Built on top of Radix UI Dialog.
When to use
Use Dialog for a centered modal that interrupts the user to gather input or confirm a non-destructive choice. For destructive confirmations (delete, irreversible actions), use AlertDialog. For side-panel content (filter panels, detail/inspector views, navigation drawers), use Sheet.
Layering and z-index
Dialog.Content renders its floating layer at Tailwind z-60, Mantle's overlay tier, above every float (z-50). Floats composed inside the content portal into the positioner (data-slot="dialog-positioner") and paint above the content. When multiple overlays are open, the most recently mounted overlay renders on top.
Dialog, Sheet, and AlertDialog each render their own backdrop, so stacking modal layers compounds the dim and blur effect on the layers underneath.
UI above an open dialog
UI that must take clicks above an open dialog, such as a consent banner, carries the overlay-prompt class. A click inside it does not count as an outside click. Add pointer-events-auto too, because the open dialog sets pointer-events: none on <body>. mantle's Toaster does both.
The class reaches pointer input only. The open dialog traps keyboard focus and hides outside content from assistive technology with aria-hidden, so a keyboard or screen reader user cannot reach the banner. Put a control that every user must reach inside the dialog content.
Handling autofill overlays
When a dialog contains form inputs, browser autofill or password-manager plugins (e.g. 1Password) may render overlays as siblings to the dialog content. By default, clicking these overlays triggers onPointerDownOutside, which closes the dialog. Use isDialogOverlayTarget to prevent this:
Composition
Compose the parts of a Dialog together to build your own:
Header with a description
Dialog.Title and Dialog.Description stack in a column inside Dialog.Header. Every other child shares the title's row. A long title truncates beside Dialog.CloseIconButton, and the description wraps under both.
The first dialog pairs a short title with a long description. The second pairs a long title with a one-line description in a wider dialog. In both, the title keeps its width and the description sits under it.
Full-page dialog
Every dialog already sits 16px from each viewport edge: Dialog.Content positions itself inside a fixed inset-4 wrapper, so you never add that margin yourself. appearance decides how much of the box it claims from there:
The three values move together as one decision, because a dialog that fills the viewport has no width left to cap and no corner left to round. preferredWidth therefore belongs to "centered" only: passing it beside "full-page" or "full-bleed" is a type error, not a prop that silently does nothing.
The parts keep their roles at every appearance. Because Dialog.Content is a flex column with Dialog.Body at flex-1 overflow-y-auto, the header and footer stay pinned while the body scrolls.
When the content needs the whole viewport, reach for this: a request log, a diff, a media preview, a multi-column editor. When the content deserves its own URL, a back button, or a shareable link, a route beats a dialog.
"full-bleed" keeps its backdrop. The content opens at zoom-in-95, so a sliver of viewport edge stays uncovered for the length of the animation, and the overlay is what fills it.
The switch in the demo body swaps "full-page" for "full-bleed".
Combining with a Tooltip
In some cases, you might wish to have a tooltip over the dialog trigger. If the dialog trigger is an IconButton, a tooltip adds context about what the button does. Compose the two so the dialog trigger is also the tooltip trigger.
Stacking a Sheet over a Dialog
Dialog and Sheet share the overlay tier (z-60), so the most recently mounted overlay renders on top. Open the dialog below first, then open the sheet from inside it: the sheet stacks over the dialog rather than disappearing behind it. Each overlay renders its own backdrop, so the dialog beneath is dimmed by the sheet's overlay.
With TanStack Form
Use TanStack Form with Field to gather validated input in a dialog. Dialog.Content is a flex column, so wrap Dialog.Body and Dialog.Footer in a display: contents form: the footer's submit button participates in the form while the body keeps its scrollable layout. Close the dialog from the form's onSubmit (it only runs once validation passes) and reset the form when the dialog closes so a cancelled draft doesn't leak into the next open. The form sets noValidate so the browser's native type="email" validation doesn't block the submit event: the schema owns validation and errors render consistently through Field.Errors.
If password managers or browser autofill overlays interfere with outside-click dismissal, see Handling autofill overlays.
Polymorphism
Dialog.Trigger, Dialog.Close, Dialog.Overlay, Dialog.Content, Dialog.Title, Dialog.Description, and Dialog.CloseIconButton accept asChild, which renders your single child in place of the part's default element while keeping its styling and behavior.
Dialog.Trigger asChild is by far the most common: it makes your own Button open the dialog instead of nesting a button inside one. Use Dialog.Close asChild to let any element dismiss the dialog, and Dialog.Title asChild when the heading level must fit the surrounding page outline.
Dialog.Root and Dialog.Portal render no DOM of their own (Root is a state provider and Portal only relocates its children), so neither takes asChild. Dialog.Header, Dialog.Body, and Dialog.Footer are plain layout <div>s that do not currently accept it.
API Reference
Dialog.Root
The root stateful component that manages the open/closed state of the dialog.
Dialog renders its floating layer at Tailwind z-60, Mantle's overlay tier, above every float (z-50). Floats composed inside the content portal into the positioner (data-slot="dialog-positioner") and paint above the content. When multiple overlays are open, the most recently mounted overlay renders on top.
Dialog.Trigger
A button that opens the dialog.
Dialog.Content
The container for the dialog content. Renders on top of the overlay and is centered in the viewport.
Dialog.Content renders its floating layer at Tailwind z-60, Mantle's overlay tier, above every float (z-50). Floats composed inside the content portal into the positioner (data-slot="dialog-positioner") and paint above the content. When multiple overlays are open, the most recently mounted overlay renders on top.
Dialog.Content renders inside a positioner element (data-slot="dialog-positioner") that lays out the content and receives floats portaled from inside the dialog. The full stacking contract lives at Stacking Layers.
Dialog.Header
Contains the title, the description, and the close button of the dialog. Dialog.Title and Dialog.Description stack in a column. Every other child shares the title's row, so a long title truncates beside Dialog.CloseIconButton. See Header with a description.
Dialog.Body
Contains the main content of the dialog.
Dialog.Footer
Contains the footer content of the dialog, including action buttons.
Dialog.Title
An accessible name to be announced when the dialog is opened.
Dialog.Description
An accessible description to be announced when the dialog is opened.
Dialog.Close
A button that closes the dialog when clicked.
Dialog.CloseIconButton
An icon button that closes the dialog when clicked.