Dialog
A popup that opens on top of the entire page.
API reference
Import the component and assemble its parts:
import { Dialog } from '@base-ui-components/react/dialog';
<Dialog.Root> <Dialog.Trigger /> <Dialog.Portal> <Dialog.Backdrop /> <Dialog.Popup> <Dialog.Title /> <Dialog.Description /> <Dialog.Close /> </Dialog.Popup> </Dialog.Portal></Dialog.Root>
Root
Groups all parts of the dialog. Doesn’t render its own HTML element.
Prop | Type | Default | |
---|---|---|---|
defaultOpen |
|
| |
open |
|
| |
onOpenChange |
|
| |
dismissible |
|
| |
modal |
|
|
Trigger
A button that opens the dialog.
Renders a <button>
element.
Prop | Type | Default | |
---|---|---|---|
className |
|
| |
render |
|
|
Attribute | Description | |
---|---|---|
data-popup-open | Present when the corresponding dialog is open. |
Portal
A portal element that moves the popup to a different part of the DOM.
By default, the portal element is appended to <body>
.
Prop | Type | Default | |
---|---|---|---|
container |
|
| |
keepMounted |
|
|
Backdrop
An overlay displayed beneath the popup.
Renders a <div>
element.
Prop | Type | Default | |
---|---|---|---|
className |
|
| |
keepMounted |
|
| |
render |
|
|
Attribute | Description | |
---|---|---|
data-open | Present when the dialog is open. | |
data-closed | Present when the dialog is closed. | |
data-starting-style | Present when the dialog is animating in. | |
data-ending-style | Present when the dialog is animating out. |
Popup
A container for the dialog contents.
Renders a <div>
element.
Prop | Type | Default | |
---|---|---|---|
initialFocus |
|
| |
finalFocus |
|
| |
className |
|
| |
keepMounted |
|
| |
render |
|
|
Attribute | Description | |
---|---|---|
data-open | Present when the dialog is open. | |
data-closed | Present when the dialog is closed. | |
data-has-nested-dialogs | Present when the dialog has other open dialogs nested within it. | |
data-nested | Present when the dialog is nested within another dialog. | |
data-starting-style | Present when the dialog is animating in. | |
data-ending-style | Present when the dialog is animating out. |
CSS Variable | Description | |
---|---|---|
--nested-dialogs | Indicates how many dialogs are nested within. |
Title
A heading that labels the dialog.
Renders an <h2>
element.
Prop | Type | Default | |
---|---|---|---|
className |
|
| |
render |
|
|
Description
A paragraph with additional information about the dialog.
Renders a <p>
element.
Prop | Type | Default | |
---|---|---|---|
className |
|
| |
render |
|
|
Close
A button that closes the dialog.
Renders a <button>
element.
Prop | Type | Default | |
---|---|---|---|
className |
|
| |
render |
|
|
Examples
State
By default, Dialog is an uncontrolled component that manages its own state.
<Dialog.Root> <Dialog.Trigger>Open</Dialog.Trigger> <Dialog.Portal> <Dialog.Popup> <Dialog.Title>Example dialog</Dialog.Title> <Dialog.Close>Close</Dialog.Close> </Dialog.Popup> </Dialog.Portal></Dialog.Root>
Use open
and onOpenChange
props if you need to access or control the state of the dialog.
For example, you can control the dialog state in order to open it imperatively from another place in your app.
const [open, setOpen] = React.useState(false);return ( <Dialog.Root open={open} onOpenChange={setOpen}> <Dialog.Trigger>Open</Dialog.Trigger> <Dialog.Portal> <Dialog.Popup> <form // Close the dialog once the form data is submitted onSubmit={async () => { await submitData(); setOpen(false); }} > ... </form> </Dialog.Popup> </Dialog.Portal> </Dialog.Root>);
It’s also common to use onOpenChange
if your app needs to do something when the dialog is closed or opened. This is recommended over React.useEffect
when reacting to state changes.
<Dialog.Root open={open} onOpenChange={(open) => { // Do stuff when the dialog is closed if (!open) { doStuff(); } // Set the new state setOpen(open); }}>
Open from a menu
In order to open a dialog using a menu, control the dialog state and open it imperatively using the onClick
handler on the menu item.
Make sure to also use the dialog’s finalFocus
prop to return focus back to the menu trigger.
import * as React from 'react';import { Dialog } from '@base-ui/components/dialog';import { Menu } from '@base-ui/components/menu';
function ExampleMenu() { const menuTriggerRef = React.useRef<HTMLButtonElement>(null); const [dialogOpen, setDialogOpen] = React.useState(false);
return ( <React.Fragment> <Menu.Root> {/* Set the trigger ref */} <Menu.Trigger ref={menuTriggerRef}>Open menu</Menu.Trigger> <Menu.Portal> <Menu.Positioner> <Menu.Popup> {/* Open the dialog when the menu item is clicked */} <Menu.Item onClick={() => setDialogOpen(true)}>Open dialog</Menu.Item> </Menu.Popup> </Menu.Positioner> </Menu.Portal> </Menu.Root>
{/* Control the dialog state */} <Dialog.Root open={dialogOpen} onOpenChange={setDialogOpen}> <Dialog.Portal> <Dialog.Backdrop /> {/* Return focus to the menu trigger when the dialog is closed */} <Dialog.Popup finalFocus={menuTriggerRef}> </Dialog.Popup> </Dialog.Portal> </Dialog.Root> </React.Fragment> );}
Nested dialogs
You can nest dialogs within one another normally.
Use the [data-has-nested-dialogs]
selector and the var(--nested-dialogs)
CSS variable to customize the styling of the parent dialog. Backdrops of the child dialogs won’t be rendered so that you can present the parent dialog in a clean way behind the one on top of it.
Close confirmation
This example shows a nested confirmation dialog that opens if the text entered in the parent dialog is going to be discarded.
To implement this, both dialogs should be controlled. The confirmation dialog may be opened when onOpenChange
callback of the parent dialog receives a request to close. This way, the confirmation is automatically shown when the user clicks the backdrop, presses the Esc key, or clicks a close button.