Dialog

Centered native <dialog> via showModal(). open is controlled, or defaultOpen leaves it uncontrolled. Escape and backdrop dismiss are opt-out. No sheet morph.

Enter is an opacity fade over 200ms. @starting-style where supported; data-entered is the fallback so unsupported engines still tween. Exit stays mounted until transitionend (160ms fallback).

Discard draft?

This cannot be undone.

The dialog stays centered on every viewport. It does not become a sheet.

Dark compact, backdrop dismiss off.

Pinned scrim

Backdrop clicks do nothing. Escape still closes.

Overlays inherit tokens from ThemeProvider / DensityProvider. They do not inherit extra classes on the provider node.

iOS may still rubber-band the page behind an open overlay. Chrome may ignore cancel.preventDefault() without user activation. State still resyncs from the native close event.

When to use

When: Centered modal task or confirm (native <dialog>, controlled open or uncontrolled defaultOpen). Name via Dialog.Title or aria-label. Escape + backdrop dismiss by default.

When not: Mobile picker from the bottom → BottomSheet. Full-height side panel → Drawer. Non-modal anchored panel → Popover. Short hint → Tooltip. Needs to become a sheet on mobile (Dialog never morphs).

Props

Dialog.Root props
NameTypeRequiredDefaultDescription
openbooleanno—Controlled open state. Leave it out for an uncontrolled dialog.
defaultOpenbooleannofalseShows the dialog on mount when uncontrolled. There is no trigger part, so once closed it opens again only on remount.
onOpenChange(open: boolean, reason: DialogCloseReason) => voidno—Dismiss requests. When controlled, staying open keeps the dialog open. Reasons: escape, backdrop, close-button, form (a <form method="dialog"> submit; the submitter value is on the element returnValue), native (something else closed the element).
dismissOnEscapebooleannotrueEscape calls onOpenChange(false, "escape").
dismissOnBackdropbooleannotrueScrim click calls onOpenChange(false, "backdrop").
aria-labelstringno—Name when Dialog.Title is omitted.
Dialog.Title props
NameTypeRequiredDefaultDescription
childrenReact.ReactNodeno—Title text.
Dialog.Description props
NameTypeRequiredDefaultDescription
childrenReact.ReactNodeno—Description text.
Dialog.Close props
NameTypeRequiredDefaultDescription
aria-labelstringno"Close" when its text, not counting aria-hidden or SVG parts, has fewer than two letters or digitsAccessible name. Text of two or more letters or digits, such as "Cancel", names the button by itself. Otherwise the close string from StringsProvider names it, Close in English.
onClickReact.MouseEventHandler<HTMLButtonElement>no—Runs before the close request. Call event.preventDefault() to keep the dialog open.