Dialog
A modal dialog surface with backdrop, focus management, and configurable dismiss behavior.
Examples
Section titled “Examples”Default
Section titled “Default”<> <Button label="Open dialog" onClick={() => setIsOpen(true)} /> <Dialog {...args} isOpen={isOpen} onOpenChange={setIsOpen}> <Layout content={ <LayoutContent> <Text as="p" color="secondary"> Are you sure you want to apply these changes to this workspace? </Text> </LayoutContent> } footer={ <LayoutFooter primaryButton={ <Button label="Apply" onClick={() => setIsOpen(false)} variant="primary" /> } secondaryButton={ <Button label="Cancel" onClick={() => setIsOpen(false)} /> } /> } header={ <LayoutHeader subtitle="Review the updates before applying them." title="Confirm changes" /> } /> </Dialog></>Without Dividers
Section titled “Without Dividers”<> <Button label="Open dialog without dividers" onClick={() => setIsOpen(true)} /> <Dialog {...args} isOpen={isOpen} onOpenChange={setIsOpen}> <Layout content={ <LayoutContent> <Text as="p" color="secondary"> Are you sure you want to apply these changes to this workspace? </Text> </LayoutContent> } footer={ <LayoutFooter primaryButton={ <Button label="Apply" onClick={() => setIsOpen(false)} variant="primary" /> } secondaryButton={ <Button label="Cancel" onClick={() => setIsOpen(false)} /> } /> } hasDividers={false} header={ <LayoutHeader subtitle="Review the updates before applying them." title="Confirm changes" /> } /> </Dialog></>Required
Section titled “Required”<> <Button label="Open required dialog" onClick={() => setIsOpen(true)} /> <Dialog {...args} isOpen={isOpen} onOpenChange={setIsOpen}> <Layout content={ <LayoutContent> <Text as="p" color="secondary"> This dialog can only be closed from an explicit action. </Text> </LayoutContent> } footer={ <LayoutFooter primaryButton={ <Button label="Acknowledge" onClick={() => setIsOpen(false)} variant="primary" /> } /> } header={<LayoutHeader title="Required action" />} /> </Dialog></><> <Button label="Open form dialog" onClick={() => setIsOpen(true)} /> <Dialog {...args} isOpen={isOpen} onOpenChange={setIsOpen}> <Layout content={ <LayoutContent> <div style={{ display: 'flex', flexDirection: 'column', gap: 12, }}> <TextInput hasAutoFocus label="Name" onChange={() => {}} value="Ada Lovelace" /> <TextInput label="Email" onChange={() => {}} type="email" value="ada@example.com" /> </div> </LayoutContent> } footer={ <LayoutFooter primaryButton={ <Button label="Save" onClick={() => setIsOpen(false)} variant="primary" /> } secondaryButton={ <Button label="Cancel" onClick={() => setIsOpen(false)} /> } /> } header={<LayoutHeader title="Edit profile" />} /> </Dialog></>Fullscreen
Section titled “Fullscreen”<> <Button label="Open fullscreen dialog" onClick={() => setIsOpen(true)} /> <Dialog {...args} isOpen={isOpen} onOpenChange={setIsOpen}> <Layout content={ <LayoutContent> <Text as="p" color="secondary"> This dialog takes up the entire viewport. </Text> </LayoutContent> } header={<LayoutHeader title="Full screen" />} /> </Dialog></>Scrollable
Section titled “Scrollable”<> <Button label="Open scrollable dialog" onClick={() => setIsOpen(true)} /> <Dialog {...args} isOpen={isOpen} onOpenChange={setIsOpen}> <Layout content={ <LayoutContent> {Array.from({length: 20}, (_, i) => ( <Text as="p" color="secondary" key={i}> Section {i + 1}: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </Text> ))} </LayoutContent> } footer={ <LayoutFooter primaryButton={ <Button label="Accept" onClick={() => setIsOpen(false)} variant="primary" /> } /> } header={<LayoutHeader title="Terms of Service" />} /> </Dialog></>Nested Overlay Escape
Section titled “Nested Overlay Escape”<> <Button label="Open nested overlays" onClick={() => setIsOpen(true)} /> <Dialog {...args} isOpen={isOpen} onOpenChange={setIsOpen}> <Layout content={ <LayoutContent> <div style={{ display: 'flex', flexDirection: 'column', gap: 16, }}> <Select label="Fruit" onChange={setFruit} options={['Apple', 'Banana', 'Cherry']} value={fruit} /> <DropdownMenu button={{label: 'Dialog actions'}} items={[ {label: 'Duplicate'}, {label: 'Archive'}, {type: 'divider'}, {label: 'Delete'}, ]} /> </div> </LayoutContent> } footer={ <LayoutFooter primaryButton={ <Button label="Done" onClick={() => setIsOpen(false)} variant="primary" /> } /> } header={<LayoutHeader title="Nested overlays" />} /> </Dialog></>Header Variants
Section titled “Header Variants”Basic — title only (no close button outside Dialog)
Basic header
With subtitle and endContent
Full header
Supporting text below the title
Draft
With all slots populated
New
Kitchen sink
All slots populated
Draft
<div style={{display: 'flex', flexDirection: 'column', gap: 24}}> <div> <Text as="p" color="secondary" type="supporting"> Basic — title only (no close button outside Dialog) </Text> <div style={{ border: '1px solid #eee', borderRadius: 8, marginTop: 8, maxWidth: 480, }}> <LayoutHeader title="Basic header" /> </div> </div> <div> <Text as="p" color="secondary" type="supporting"> With subtitle and endContent </Text> <div style={{ border: '1px solid #eee', borderRadius: 8, marginTop: 8, maxWidth: 480, }}> <LayoutHeader endContent={<Badge color="info" label="Draft" size="sm" />} subtitle="Supporting text below the title" title="Full header" /> </div> </div> <div> <Text as="p" color="secondary" type="supporting"> With all slots populated </Text> <div style={{ border: '1px solid #eee', borderRadius: 8, marginTop: 8, maxWidth: 480, }}> <LayoutHeader endContent={<Badge color="info" label="Draft" size="sm" />} startContent={<Badge color="success" label="New" size="sm" />} subtitle="All slots populated" title="Kitchen sink" /> </div> </div></div>Imperative
Section titled “Imperative”<> <Button label="Show report" onClick={() => dialog.show( <Layout content={ <LayoutContent> <Text as="p" color="secondary"> The report is ready for review. </Text> </LayoutContent> } header={<LayoutHeader title="Generated report" />} />, ) } /> {dialog.element}</>Dialog
Section titled “Dialog”A modal dialog surface with backdrop, focus management, and configurable dismiss behavior.
| Prop | Type | Default | Description |
|---|---|---|---|
children* | ReactNode | — | Dialog body content. |
className | string | — | Additional CSS class names applied to the dialog. |
data-testid | string | — | Test ID applied to the dialog. |
dismissBehavior | DialogDismissBehavior | {isEscapeDismissEnabled: true, isBackdropDismissEnabled: true} | Controls whether Escape and backdrop clicks request dismissal. Pass a boolean to enable or disable both behaviors together. |
isOpen* | boolean | — | Whether the dialog is open. |
label | string | — | Accessible label for the dialog. When provided, sets aria-label directly. When omitted, the dialog uses aria-labelledby to reference the heading rendered by a child LayoutHeader. Omit this prop when using a LayoutHeader inside the dialog. Set it when the dialog has no visible heading. |
maxHeight | number | string | '75vh' | Maximum height of the dialog. Numbers are treated as pixels. |
onOpenChange* | (isOpen: boolean) => void | — | Called when the dialog requests an open-state change. |
position | Readonly<DialogPosition> | — | Fixed positioning offsets for the dialog. |
ref | Ref<HTMLDialogElement> | — | Ref forwarded to the dialog element. |
role | "dialog" | "alertdialog" | 'dialog' | ARIA role exposed by the dialog. |
style | CSSProperties | — | Inline styles applied to the dialog. |
variant | "fullscreen" | "standard" | 'standard' | Display variant. |
width | number | string | 400 | Dialog width. Numbers are treated as pixels. |