Dialog
A modal window over a dimmed page, wrapping the Base UI Dialog. Focus is trapped inside and returned on close, the window scrolls when taller than the screen, and Escape always works. For a panel that doesn't take over the page, reach for the Popover.
Playground
Open the dialog and try Escape, a click on the backdrop, and tabbing past the last button; switch off Outside click to see the backdrop stop dismissing while the keyboard still does.
States
A dialog is closed, opening, open, or closing, and only the trigger can be shown at rest: while open it carries data-popup-open, the popup and backdrop pass through their enter and exit transitions on the motion tokens, and hover and focus inside the window belong to the controls you put there. With modal={false} the page behind stays interactive and undimmed; "trap-focus" keeps the trap without the dim.
Styling states
The popup and backdrop both carry data-starting-style and data-ending-style during the enter and exit transitions, and the trigger carries data-popup-open while open, so a wider window or a pressed-looking trigger needs no JavaScript:
.ub-dialog-popup {
max-width: 32rem;
}
.ub-dialog-trigger[data-popup-open] {
background: var(--ub-bg-muted);
}Props
Open state lives on the root; the title and description wire the dialog's accessible name and description automatically. The essentials:
| Prop | Type | Default | Description |
|---|---|---|---|
DialogRoot.open | boolean | – | Controlled state. Pair with onOpenChange (also defaultOpen). |
DialogRoot.modal | boolean | "trap-focus" | true | Dims and inerts the page behind; false keeps it interactive, trap-focus traps focus without dimming. |
DialogRoot.disablePointerDismissal | boolean | false | Ignore outside clicks, for forms that shouldn't be lost. |
DialogTrigger.render | ReactElement | – | The real control that opens the dialog, usually a Bones Button. |
DialogContent.initialFocus | boolean | RefObject<HTMLElement | null> | (openType) => ... | – | Where focus lands on open; defaults to the first focusable part. |
DialogContent.finalFocus | RefObject<HTMLElement | null> | – | Where focus returns on close; defaults to the trigger. |
One quirk: the accessible name comes from DialogTitle, so keep one even in a tiny confirm; without it the dialog is announced as an unnamed dialog.
Agent instructions
Working with a coding agent? Paste this into its context, or point it at llms.txt for the whole library.
DialogRoot, DialogTrigger, DialogContent, DialogTitle, DialogDescription, DialogClose, from @usebones/react.
- Structure: DialogRoot wraps DialogTrigger + DialogContent; put DialogTitle (names the dialog, always include one), DialogDescription, and the body inside the content. DialogClose closes it; render several for Cancel/Save pairs.
- Attach trigger and close buttons to real controls via render={<Button ... />}.
- Modal by default: page dimmed and inert, focus trapped, Escape closes, focus returns to the trigger. modal={false} keeps the page interactive; disablePointerDismissal ignores outside clicks for forms.
- The content scrolls when taller than the screen; no height handling needed.
- Restyle in CSS via .ub-dialog-popup (26rem max-width default), .ub-dialog-backdrop, .ub-dialog-title, .ub-dialog-description, [data-starting-style]/[data-ending-style] for enter/exit, [data-popup-open] on the trigger. Tokens only.