import{ Button, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger }from'@rookcom/monarch-react';<Dialog><DialogTrigger asChild><Buttonvariant="danger">Delete project</Button></DialogTrigger><DialogContentsize="sm"><DialogHeader><DialogTitle>Delete project?</DialogTitle><DialogDescription>
This removes all tasks and sprints. It cannot be undone.</DialogDescription></DialogHeader><DialogBody>Type the project name to confirm.</DialogBody><DialogFooter><DialogClose asChild><Button>Cancel</Button></DialogClose><Buttonvariant="danger">Delete</Button></DialogFooter></DialogContent></Dialog>
Usage examples
Form with a busy save
import{ useState }from'react';import{ Button, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, Field, FieldLabel, Input }from'@rookcom/monarch-react';function RenameDialog(){const[open, setOpen]= useState(false);const[saving, setSaving]= useState(false);const save =()=>{
setSaving(true);
window.setTimeout(()=>{
setSaving(false);
setOpen(false);},1200);};return(<Dialogopen={open}onOpenChange={setOpen}><DialogTrigger asChild><Button>Rename board</Button></DialogTrigger><DialogContentbusy={saving}closeOnOutsideClick={false}
fullScreenOnPhone
footer={<><DialogClose asChild><Buttondisabled={saving}>Cancel</Button></DialogClose><Buttonvariant="primary"loading={saving}onClick={save}>
Save
</Button></>}><DialogHeader><DialogTitle>Rename board</DialogTitle><DialogDescription>Everyone with access sees the new name.</DialogDescription></DialogHeader><DialogBody><Field><FieldLabel>Board name</FieldLabel><InputdefaultValue="Q3 launch"/></Field></DialogBody></DialogContent></Dialog>);}<RenameDialog/>
Purpose
A modal window for a focused task or decision that must be finished or dismissed before returning to the page.
Use when
A short form that should not leave the page.
A focused decision with custom content.
Avoid when
A yes/no confirmation: use ConfirmDialog, which adds the alertdialog role, pending and error states.
Detail views or longer forms beside the page: use Sheet.
Phone-first flows that should rise from the bottom: use ResponsiveDialog.
Non-blocking supplementary content: use Popover.
Long or multi-step work: use a page.
Design principles
Every dialog has a DialogTitle (visually hidden if necessary) so it has an accessible name.
Header, body and footer are separate parts so long content scrolls while actions stay visible.
Dismissal is predictable: Escape and the close button always work unless an action is running (busy); outside clicks can be turned off for forms with typed values.
One Escape press closes one layer: a control whose own popup is open keeps the press (escapeGuard).
Dialog, DialogTrigger and DialogClose are the Radix Dialog Root, Trigger and Close.
DialogContent renders in a portal with the overlay and an optional built-in close button.
footer is shorthand for a trailing <DialogFooter>; the parts still work on their own.
Generated from the TypeScript declarations of packages/react/src/components/overlays/dialog.
Properties
Dialog
Modal dialog. Focus is trapped inside, Escape closes it, and focus returns
to the trigger. Every dialog needs a DialogTitle (visually hidden if
necessary) so it has an accessible name.
Prop
Type
Description
children
React.ReactNode
DialogBody
Prop
Type
Description
children
ReactNode
className
string
id
string
DialogClose
Prop
Type
Description
asChild
boolean
children
ReactNode
className
string
disabled
boolean
id
string
DialogContent
Prop
Type
Description
asChild
boolean
busy
boolean
An action is in progress: Escape, outside clicks and the built-in close
button no longer close the dialog, and the content is `aria-busy`.
Disable your own Cancel button too.
children
ReactNode
className
string
closeLabel
string | null
Accessible label of the built-in close button. Pass null to hide the button.
closeOnOutsideClick
boolean
Whether a click outside the dialog closes it. Default `true`; turn it off
for forms with typed values so a stray click does not lose them. Escape
and the close button still work.
escapeGuard
boolean
Ignore Escape while focus is on a control whose own popup is open (a
combobox or menu button with `aria-expanded="true"`), so that press only
closes the popup. Default `true`.
footer
ReactNode
Actions rendered in a DialogFooter after the children. The footer stays
visible while DialogBody scrolls.
fullScreenOnPhone
boolean
Below 640px wide, fill the whole screen instead of floating with a margin.
id
string
size
"sm" | "md" | "lg" | "xl"
Width preset: 24rem, 32rem (default), 44rem or 60rem, capped at the viewport minus 2rem.
DialogDescription
Prop
Type
Description
asChild
boolean
children
ReactNode
className
string
id
string
DialogFooter
Prop
Type
Description
children
ReactNode
className
string
id
string
DialogHeader
Prop
Type
Description
children
ReactNode
className
string
id
string
DialogTitle
Prop
Type
Description
asChild
boolean
children
ReactNode
className
string
id
string
DialogTrigger
Prop
Type
Description
asChild
boolean
children
ReactNode
className
string
disabled
boolean
id
string
Variants
Prop
Values
Description
size (DialogContent)
'sm''md''lg''xl'
Width 24rem, 32rem (default), 44rem or 60rem, never wider than the viewport minus 2rem. Exposed as data-size.
fullScreenOnPhone (DialogContent)
'false''true'
Below 640px the dialog fills the screen with safe-area padding. Exposed as data-fullscreen-phone.
States
State
Exposed as
Behaviour
Open / closed
data-state
From Radix; content enters with the mn-dialog-in animation.
Without close button
–
closeLabel={null} removes the built-in close button.
Busy
data-busy, aria-busy
busy blocks Escape, outside clicks and the built-in close button while an action runs.
Interaction behaviour
Opening moves focus into the dialog; Tab and Shift+Tab stay inside it.
Escape, the built-in close button or any DialogClose closes it, and focus returns to the trigger.
A click on the overlay closes it, unless closeOnOutsideClick={false}.
While busy, nothing closes it; disable your own Cancel button as well.
Escape is ignored while focus is on a combobox or menu button whose popup is open (escapeGuard, on by default); Radix popups inside the dialog are already stacked layers.
Controlled with open + onOpenChange, or uncontrolled.
Keyboard support
Keys
Action
Enter / Space
On the trigger, opens the dialog.
Tab / Shift+Tab
Cycles focus within the dialog.
Escape
Closes the dialog and returns focus to the trigger.
Accessibility
Role
dialog
Accessible name
DialogTitle; DialogDescription becomes the accessible description.
The built-in close button is named by closeLabel (default "Close").
Content outside the dialog is hidden from assistive technology while open (Radix).
While busy, the content has aria-busy="true" and the close button is disabled.
For confirmations of destructive actions use ConfirmDialog, which sets role="alertdialog".
Responsive behaviour
Width is min(size, 100vw - 2rem) and height at most 100dvh - 2rem, so the dialog fits small screens.
With fullScreenOnPhone, below 640px it fills the screen and pads for notches and home bars (env(safe-area-inset-*)).
The body scrolls; header and footer stay put.
Theme support
Raised surface, border and shadow tokens; light, dark and more-contrast themes apply automatically.
The entrance animation uses duration tokens, which are 0ms under prefers-reduced-motion.
Do and don't
Do
Always include a DialogTitle.
Put the confirming action last in the footer.
Set busy while saving so a stray Escape cannot interrupt it.
Turn off outside clicks for forms with typed values.
Don't
Do not open a dialog from inside another dialog.
Do not hide the only way to close it.
Do not leave busy on after an error; let the person retry or cancel.
Related components
ConfirmDialog: Ready-made confirmation built on Dialog.