shadcn.io is not affiliated with official shadcn/ui
Focus stays in a window over the page until the short task is done. Escape or Cancel puts it back on the button that opened the window.
Use it for a task with a start and a finish, like a profile edit or a share link. A destructive confirm that must not close on an outside click belongs in an Alert Dialog. In React, Base UI traps focus and restores it when the window closes.
Installation
Anatomy

DialogTrigger
DialogOverlay
DialogPositioner
DialogContent
DialogTitle
DialogDescription
DialogCloseUsage
import { Button } from "@/components/ui/button";
import {
Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter,
DialogHeader, DialogTitle, DialogTrigger,
} from "@/components/ui/dialog";const DialogDemo = () => (
<Dialog>
<form>
<DialogTrigger render={<Button variant="outline" />}>
Open Dialog
</DialogTrigger>
<DialogContent className="sm:max-w-sm">
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>
Make changes to your profile here. Click save when you're done.
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-4">
<label className="flex flex-col gap-2 text-sm">
Name
<input
className="h-9 rounded-lg border border-input bg-transparent px-3"
defaultValue="Pedro Duarte"
name="name"
type="text"
/>
</label>
<label className="flex flex-col gap-2 text-sm">
Username
<input
className="h-9 rounded-lg border border-input bg-transparent px-3"
defaultValue="@peduarte"
name="username"
type="text"
/>
</label>
</div>
<DialogFooter>
<DialogClose render={<Button variant="outline" />}>
Cancel
</DialogClose>
<Button type="submit">Save changes</Button>
</DialogFooter>
</DialogContent>
</form>
</Dialog>
);
Accessibility
- Enter
- When focus is on the trigger, opens the dialog.
- Tab
- Moves focus to the next focusable element within the content. Focus is trapped within the dialog.
- Shift+Tab
- Moves focus to the previous focusable element. Focus is trapped within the dialog.
- EscEscape
- Closes the dialog and moves focus to trigger or the defined final focus element.
- Space
- Activate the focused control.
Demos
Close Button
A share dialog shows a read-only link and a Close button in the footer, wired with DialogClose. The corner icon is still there, and the labeled button is a second dismiss, aligned with the link. Use a labeled close when Close should read as an action, not only as an X. A plain footer button does not dismiss unless it is a DialogClose or you close the dialog yourself.
No Close Button
showCloseButton set to false removes the corner icon. This window has no other buttons, so Escape and an outside click are the way out. Hide the icon when it crowds a short title, not to force a choice. With Escape off too, a dialog that has no action is a trap, so a question people must answer belongs in an Alert Dialog with a real button.
Controlled
open lives in state. Save writes the name shown under the trigger, then closes, while Cancel still goes through DialogClose. Hold the open state when closing depends on a successful save, so a failed request can leave the window up with the error in it. Do not hang the save handler on DialogClose, because that dismisses before you know the request worked.
Sticky Footer
The paragraphs scroll inside a capped region, and the footer sits outside that scroller, so Close stays on screen. Use a sticky footer for terms, a long form, or any window where the action would otherwise scroll away. The header stays put for the same reason: it is not inside the scroller. Give that region a max height, or a dialog that grows with the page stops behaving like one.
Scrollable Content
The same scroll region, without a footer. The title and description stay visible while the paragraphs move. Use it for a note people read and then dismiss with the corner icon or Escape. If there is an action they must reach, pin it in a footer instead of leaving it under the prose, and keep a cap on the scroller so the window stays on screen.
Rtl
The content sets dir to rtl, and the copy is Arabic: title, fields, Cancel, and save. The close icon and the footer follow the inline end. Set dir on DialogContent when the window can be Arabic or Hebrew even if the trigger was drawn left to right. Do not shove the footer with a physical left margin, because logical alignment already moves the actions.
FAQ
Should I use a Dialog or an Alert Dialog?
Use a Dialog for a task: a form, a share link, a settings panel. When the only question is to confirm or cancel, like deleting a file, use an Alert Dialog, which doesn't close on an outside click. For content beside the page, a Sheet fits better.
How do I open and close a Dialog from code in React?
Hold the open state yourself, which also lets you close the dialog after an action, as in the Controlled demo.
Pass open and update it in onOpenChange, which receives the new boolean.
How do I close a Dialog after a form is saved?
Control the open state, disable the submit button while the request runs, and set the state to closed once it succeeds, so the dialog stays open if the save fails. Don't put the save on DialogClose, which closes before the request finishes.
How do I stop a Dialog from closing on an outside click or Escape?
Pass disablePointerDismissal for outside clicks, and ignore the close in onOpenChange to keep Escape from closing it.
Keep a visible way out, like a Cancel button.
How do I hide the close button in the corner of a Dialog?
Set showCloseButton to false on DialogContent, as in the No Close Button demo. It only hides the button: an outside click and Escape still close the dialog, so add your own close button or Cancel action in the footer.
Does a Dialog need a title for accessibility?
Yes. Every dialog needs a DialogTitle, which screen readers announce when it opens. When the design has no visible title, keep it and add the sr-only class. Focus moves into the dialog on open, stays inside while it's open, and goes back to the trigger when it closes.
How do I make a long Dialog scroll with a sticky footer?
Keep the header and footer outside the scrolling part and let the content between them scroll, as in the Sticky Footer and Scrollable Content demos.
API Reference
Dialog
data-slot="dialog"
open?boolean
Whether the dialog is currently open.
defaultOpen?boolean
Whether the dialog is initially open.
To render a controlled dialog, use the open prop instead.
- Default
false
onOpenChange?(open: boolean, eventDetails: DialogRoot.ChangeEventDetails) → void
Event handler called when the dialog is opened or closed.
- Type
(open: boolean, eventDetails: DialogRoot.ChangeEventDetails) → void- Parameters
open– booleaneventDetails– DialogRoot.ChangeEventDetails- Returns
void
actionsRef?React.RefObject<DialogRoot.Actions | null>
A ref to imperative actions.
- unmount: Ends the closing phase of the dialog after an externally controlled closing animation finishes.
Call preventUnmountOnClose() in onOpenChange first, otherwise the dialog completes closing on its own.
Whether it leaves the DOM is decided by keepMounted on the portal.
- close: Closes the dialog imperatively when called.
- Type
React.RefObject<DialogRoot.Actions | null>
children?React.ReactNode | PayloadChildRenderFunction<Payload>
The content of the dialog.
This can be a regular React node or a render function that receives the payload of the active trigger.
- Type
React.ReactNode | PayloadChildRenderFunction<Payload>
defaultTriggerId?string | null
ID of the trigger that the dialog is associated with.
This is useful in conjunction with the defaultOpen prop to create an initially open dialog.
- Type
string | null
disablePointerDismissal?boolean
Whether to prevent the dialog from closing on outside presses. For non-modal dialogs, this also prevents the dialog from closing when focus moves outside of it.
- Default
false
handle?DialogHandle<Payload>
A handle to associate the dialog with a trigger. If specified, allows external triggers to control the dialog's open state. Can be created with the Dialog.createHandle() method.
- Type
DialogHandle<Payload>
modal?boolean | "trap-focus"
Determines if the dialog enters a modal state when open.
- true: user interaction is limited to just the dialog: focus is trapped, document page scroll is locked, and pointer interactions on outside elements are disabled.
- false: user interaction with the rest of the document is allowed.
- trap-focus: focus is trapped inside the dialog, but document page scroll is not locked and pointer interactions outside of it remain enabled.
When modal is true or trap-focus, render <Dialog.Close> inside <Dialog.Popup> so
touch screen readers can escape the popup.
- Type
boolean | "trap-focus"- Default
true
onOpenChangeComplete?(open: boolean) → void
Event handler called after any animations complete when the dialog is opened or closed.
- Parameters
open– boolean- Returns
void
triggerId?string | null
ID of the trigger that the dialog is associated with.
This is useful in conjunction with the open prop to create a controlled dialog.
There's no need to specify this prop when the dialog is uncontrolled (that is, when the open prop is not set).
- Type
string | null
asChild?boolean
Use the provided child element as the default rendered element, combining their props and behavior.
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
DialogTrigger
<button> data-slot="dialog-trigger"
handle?DialogHandle<Payload>
A handle to associate the trigger with a dialog. Can be created with the Dialog.createHandle() method.
- Type
DialogHandle<Payload>
id?string
ID of the trigger. In addition to being forwarded to the rendered element,
it is also used to specify the active trigger for the dialog in controlled mode (with the DialogRoot triggerId prop).
nativeButton?boolean
Whether the component renders a native <button> element when replacing it
via the render prop.
Set to false if the rendered element is not a button (for example, <div>).
- Default
true
payload?Payload
A payload to pass to the dialog when it is opened.
asChild?boolean
Use the provided child element as the default rendered element, combining their props and behavior.
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
DialogOverlay
<div> data-slot="dialog-overlay"
forceRender?boolean
Whether the backdrop is forced to render even when nested.
- Default
false
asChild?boolean
Use the provided child element as the default rendered element, combining their props and behavior.
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
data-state"open" | "closed"
"open" or "closed".
- Type
"open" | "closed"
DialogTitle
<h2> data-slot="dialog-title"
asChild?boolean
Use the provided child element as the default rendered element, combining their props and behavior.
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
DialogDescription
<p> data-slot="dialog-description"
asChild?boolean
Use the provided child element as the default rendered element, combining their props and behavior.
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
DialogPortal
<div> data-slot="dialog-portal"
container?HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null
A parent element to render the portal element into.
- Type
HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null- Default
preview ?? undefined
keepMounted?boolean
Whether to keep the portal mounted in the DOM while the popup is hidden.
- Default
false
asChild?boolean
Use the provided child element as the default rendered element, combining their props and behavior.
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
DialogClose
<button> data-slot="dialog-close"
nativeButton?boolean
Whether the component renders a native <button> element when replacing it
via the render prop.
Set to false if the rendered element is not a button (for example, <div>).
- Default
true
asChild?boolean
Use the provided child element as the default rendered element, combining their props and behavior.
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
DialogContent
data-slot="dialog-content"
bottomStickOnMobile?boolean
Stick the dialog to the bottom of the screen on mobile.
- Default
true
positionerClassName?string
Class names on the positioner (the viewport-fixed wrapper).
showCloseButton?boolean
Show close button at the top right corner.
- Default
true
size?"2xl" | "3xl" | "4xl" | "5xl" | "6xl" | "fullscreen" | "lg" | "md" | "sm" | "xl"
The size.
- Type
"2xl" | "3xl" | "4xl" | "5xl" | "6xl" | "fullscreen" | "lg" | "md" | "sm" | "xl"- Default
"md"
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
DialogHeader
<div> data-slot="dialog-header"
description?string
The description of the dialog.
title?string
The title of the dialog.
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
DialogFooter
<div> data-slot="dialog-footer"
showCloseButton?boolean
Show a close button in the footer.
- Default
false
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.