Dialog
A modal overlay for focused tasks and decisions.
Overview
A dialog is a modal surface that appears in front of app content to provide
critical information or prompt for a decision. It is built on the Base UI Dialog
primitive — focus trap, scroll-lock and Esc dismissal are handled for
you — and styled to the Material-3 surface: surface-container-high, 28px
corners and the elevation-3 shadow.
Compose it from DialogTrigger, DialogContent, DialogHeader, DialogTitle,
DialogDescription and DialogFooter. Actions are plain Buttons — the basic
dialog has no close "X"; it relies on the footer actions.
$ npx morpha-ui add dialogComposition
<Dialog>
<DialogTrigger render={<Button variant="tonal">Open</Button>} />
<DialogContent>
<DialogHeader>
<DialogIcon /> {/* optional, align="center" only */}
<DialogTitle />
<DialogDescription />
</DialogHeader>
<DialogBody /> {/* optional — the scrolling region */}
<DialogFooter>
<DialogClose render={<Button variant="text">Cancel</Button>} />
</DialogFooter>
</DialogContent>
</Dialog>The fullscreen variant swaps the header for DialogTopBar (leading close,
flexible title, trailing action) and keeps DialogBody for the content.
Examples
With icon (centered)
Set align="center" on DialogContent and add a DialogIcon above the title.
The headline and supporting text center to match the Figma "icon" layout.
Without headline
Drop the visible title when the supporting text says everything (the Figma
"show title = false" variant). Keep a DialogTitle with sr-only so the dialog
still has an accessible name.
Single action
Acknowledgement dialogs take one action instead of the usual pair — the Figma "show action 2 = false" variant. The footer keeps the same right-aligned rhythm.
With close button
Pass showCloseButton to add the top-right "X" (an IconButton). It is off by
default: the Figma dialog dismisses through its actions.
With a form
Wrap the header, fields and footer in a <form> and let the submit button close
the dialog. Fields use the standard Field / InputGroup parts on the same 24px
gutter as the header.
Scrollable content
Put long content in DialogBody; it scrolls while the header and footer stay
fixed. Add divided to draw the dividers that separate the scroll region.
Controlled
Drive the dialog yourself with open / onOpenChange — useful when an action
must run before it closes, or when there is no trigger to click at all.
Not subscribed yet.
Alert (non-dismissible)
Pass dismissible={false} for confirmations that must be answered: clicking the
scrim or pressing Esc no longer closes it, and the content is exposed
as role="alertdialog". Pair it with a destructive action.
Full-screen
variant="fullscreen" fills the viewport (no radius or shadow) and slides up
from the bottom — the mobile pattern. Use DialogTopBar for the header (leading
close, flexible title, trailing action) and DialogBody for the content.
RTL
Wrap the app in DirectionProvider and set dir="rtl". The header, supporting
text and the footer actions all mirror — the paddings are logical, not physical.
Because the content is portalled, set dir on DialogContent too (or on
<html>).
Coming from shadcn? There is no separate alert-dialog component —
pass dismissible={false} to DialogContent for alert semantics
(role="alertdialog", no outside-click / Esc close).
Installation
Morpha UI components are distributed via the Morpha CLI: npx morpha-ui add dialog.
npx morpha-ui add dialogAPI Reference
DialogContent adds the props below; all Base UI Dialog.Popup props are
forwarded. The other parts — DialogTrigger, DialogHeader, DialogIcon,
DialogBody, DialogFooter, DialogTopBar, DialogTitle, DialogDescription
and DialogClose — forward their Base UI / div props. DialogBody takes one
extra prop, divided, which draws the top/bottom dividers around the scrolling
region.
Prop
Type
The Dialog root:
Prop
Type
Accessibility
- A
DialogTitleis required (wrap it insr-onlyif you don't want it shown) so the dialog has an accessible name. - Link explanatory text with
DialogDescription— Base UI wiresaria-describedbyautomatically when one is present, and omits it otherwise (no workaround needed for layouts without one). - Focus is trapped while open and restored on close; Esc dismisses the
dialog (handled by Base UI) — unless
dismissible={false}, which keeps it open and marks itrole="alertdialog". - The enter animation fades and scales from 0.95; under
prefers-reduced-motiononly the fade remains (no zoom or slide).
The dialog content is rendered in a portal on document.body, so inside the
preview it follows the site theme rather than the frame's light/dark toggle.