data/primitives/docs/components/dialog.mdx
<Highlights features={[ "Supports modal and non-modal modes.", "Focus is automatically trapped within modal.", "Can be controlled or uncontrolled.", <span> Manages screen reader announcements with <Code>Title</Code> and{" "} <Code>Description</Code> components. </span>, "Esc closes the component automatically.", ]} />
Install the component from your command line.
npm install @radix-ui/react-dialog
Import all parts and piece them together.
import { Dialog } from "radix-ui";
export default () => (
<Dialog.Root>
<Dialog.Trigger />
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content>
<Dialog.Title />
<Dialog.Description />
<Dialog.Close />
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
Contains all the parts of a dialog.
<PropsTable data={[ { name: "defaultOpen", type: "boolean", description: ( <span> The open state of the dialog when it is initially rendered. Use when you do not need to control its open state. </span> ), }, { name: "open", type: "boolean", description: ( <span> The controlled open state of the dialog. Must be used in conjunction with <Code>onOpenChange</Code>. </span> ), }, { name: "onOpenChange", type: "(open: boolean) => void", typeSimple: "function", description: ( <span> Event handler called when the open state of the dialog changes. </span> ), }, { name: "modal", required: false, type: "boolean", default: "true", description: ( <span> The modality of the dialog. When set to <Code>true</Code>, interaction with outside elements will be disabled and only dialog content will be visible to screen readers. </span> ), }, ]} />
The button that opens the dialog.
<PropsTable data={[ { name: "asChild", required: false, type: "boolean", default: "false", description: ( <> Change the default rendered element for the one passed as a child, merging their props and behavior.
Read our <a href="../guides/composition">Composition</a> guide for
more details.
</>
),
},
]}
/>
<DataAttributesTable data={[ { attribute: "[data-state]", values: ["open", "closed"], }, ]} />
When used, portals your overlay and content parts into the body.
<PropsTable data={[ { name: "forceMount", type: "boolean", description: ( <span> Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. If used on this part, it will be inherited by <Code>Dialog.Overlay</Code> and{" "} <Code>Dialog.Content</Code>. </span> ), }, { name: "container", type: "HTMLElement", default: "document.body", description: "Specify a container element to portal the content into.", }, ]} />
A layer that covers the inert portion of the view when the dialog is open.
<PropsTable data={[ { name: "asChild", required: false, type: "boolean", default: "false", description: ( <> Change the default rendered element for the one passed as a child, merging their props and behavior.
Read our <a href="../guides/composition">Composition</a> guide for
more details.
</>
),
},
{
name: "forceMount",
type: "boolean",
description: (
<span>
Used to force mounting when more control is needed. Useful when
controlling animation with React animation libraries. It inherits from{" "}
<Code>Dialog.Portal</Code>.
</span>
),
},
]}
/>
<DataAttributesTable data={[ { attribute: "[data-state]", values: ["open", "closed"], }, ]} />
Contains content to be rendered in the open dialog.
<PropsTable data={[ { name: "asChild", required: false, type: "boolean", default: "false", description: ( <> Change the default rendered element for the one passed as a child, merging their props and behavior.
Read our <a href="../guides/composition">Composition</a> guide for
more details.
</>
),
},
{
name: "forceMount",
type: "boolean",
description: (
<span>
Used to force mounting when more control is needed. Useful when
controlling animation with React animation libraries. It inherits from{" "}
<Code>Dialog.Portal</Code>.
</span>
),
},
{
name: "onOpenAutoFocus",
type: "(event: Event) => void",
typeSimple: "function",
description: (
<span>
Event handler called when focus moves into the component after
opening. It can be prevented by calling{" "}
<Code>event.preventDefault</Code>.
</span>
),
},
{
name: "onCloseAutoFocus",
type: "(event: Event) => void",
typeSimple: "function",
description: (
<span>
Event handler called when focus moves to the trigger after closing. It
can be prevented by calling <Code>event.preventDefault</Code>.
</span>
),
},
{
name: "onEscapeKeyDown",
type: "(event: KeyboardEvent) => void",
typeSimple: "function",
description: (
<span>
Event handler called when the escape key is down. It can be prevented
by calling <Code>event.preventDefault</Code>.
</span>
),
},
{
name: "onPointerDownOutside",
type: "(event: PointerDownOutsideEvent) => void",
typeSimple: "function",
description: (
<span>
Event handler called when a pointer event occurs outside the bounds of
the component. It can be prevented by calling{" "}
<Code>event.preventDefault</Code>.
</span>
),
},
{
name: "onInteractOutside",
type: "(event: React.FocusEvent | MouseEvent | TouchEvent) => void",
typeSimple: "function",
description: (
<span>
Event handler called when an interaction (pointer or focus event)
happens outside the bounds of the component. It can be prevented by
calling <Code>event.preventDefault</Code>.
</span>
),
},
]}
/>
<DataAttributesTable data={[ { attribute: "[data-state]", values: ["open", "closed"], }, ]} />
The button that closes the dialog.
<PropsTable data={[ { name: "asChild", required: false, type: "boolean", default: "false", description: ( <> Change the default rendered element for the one passed as a child, merging their props and behavior.
Read our <a href="../guides/composition">Composition</a> guide for
more details.
</>
),
},
]}
/>
An accessible title to be announced when the dialog is opened.
If you want to hide the title, wrap it inside our Visually Hidden utility like this <VisuallyHidden asChild>.
<PropsTable data={[ { name: "asChild", required: false, type: "boolean", default: "false", description: ( <> Change the default rendered element for the one passed as a child, merging their props and behavior.
Read our <a href="../guides/composition">Composition</a> guide for
more details.
</>
),
},
]}
/>
An optional accessible description to be announced when the dialog is opened.
If you want to hide the description, wrap it inside our Visually Hidden utility like this <VisuallyHidden asChild>. If you want to remove the description entirely, remove this part and pass aria-describedby={undefined} to Dialog.Content.
<PropsTable data={[ { name: "asChild", required: false, type: "boolean", default: "false", description: ( <> Change the default rendered element for the one passed as a child, merging their props and behavior.
Read our <a href="../guides/composition">Composition</a> guide for
more details.
</>
),
},
]}
/>
Use the controlled props to programmatically close the Dialog after an async operation has completed.
import * as React from "react";
import { Dialog } from "radix-ui";
const wait = () => new Promise((resolve) => setTimeout(resolve, 1000));
export default () => {
const [open, setOpen] = React.useState(false);
return (
<Dialog.Root __open__={open} __onOpenChange__={setOpen}>
<Dialog.Trigger>Open</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content>
<form
onSubmit={(event) => {
wait().then(() => setOpen(false));
event.preventDefault();
}}
>
<button type="submit">Submit</button>
</form>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
};
Move the content inside the overlay to render a dialog with overflow.
// index.jsx
import { Dialog } from "radix-ui";
import "./styles.css";
export default () => {
return (
<Dialog.Root>
<Dialog.Trigger />
<Dialog.Portal>
<Dialog.Overlay className="DialogOverlay">
<Dialog.Content className="DialogContent">...</Dialog.Content>
</Dialog.Overlay>
</Dialog.Portal>
</Dialog.Root>
);
};
/* styles.css */
.DialogOverlay {
background: rgba(0 0 0 / 0.5);
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: grid;
place-items: center;
overflow-y: auto;
}
.DialogContent {
min-width: 300px;
background: white;
padding: 30px;
border-radius: 4px;
}
Customise the element that your dialog portals into.
import * as React from "react";
import { Dialog } from "radix-ui";
export default () => {
const [container, setContainer] = React.useState(null);
return (
<div>
<Dialog.Root>
<Dialog.Trigger />
<Dialog.Portal __container__={container}>
<Dialog.Overlay />
<Dialog.Content>...</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
<div ref={__setContainer__} />
</div>
);
};
Adheres to the Dialog WAI-ARIA design pattern.
<KeyboardTable data={[ { keys: ["Space"], description: "Opens/closes the dialog.", }, { keys: ["Enter"], description: "Opens/closes the dialog.", }, { keys: ["Tab"], description: "Moves focus to the next focusable element.", }, { keys: ["Shift + Tab"], description: "Moves focus to the previous focusable element.", }, { keys: ["Esc"], description: ( <span> Closes the dialog and moves focus to <Code>Dialog.Trigger</Code>. </span> ), }, ]} />
Create your own API by abstracting the primitive parts into your own component.
This example abstracts the Dialog.Overlay and Dialog.Close parts.
import { Dialog, DialogTrigger, DialogContent } from "./your-dialog";
export default () => (
<Dialog>
<DialogTrigger>Dialog trigger</DialogTrigger>
<DialogContent>Dialog Content</DialogContent>
</Dialog>
);
// your-dialog.jsx
import * as React from "react";
import { Dialog as DialogPrimitive } from "radix-ui";
import { Cross1Icon } from "@radix-ui/react-icons";
export const DialogContent = React.forwardRef(
({ children, ...props }, forwardedRef) => (
<DialogPrimitive.Portal>
<DialogPrimitive.Overlay />
<DialogPrimitive.Content {...props} ref={forwardedRef}>
{children}
<DialogPrimitive.Close aria-label="Close">
<Cross1Icon />
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPrimitive.Portal>
),
);
export const Dialog = DialogPrimitive.Root;
export const DialogTrigger = DialogPrimitive.Trigger;