Dialog
A window overlaid on the primary window, rendering content.
When to Use
Use Dialog when you need to:
- Collect user input via forms (edit profile, create item, settings)
- Display supplementary content that requires focused attention
- Present a multi-step workflow without navigating away from the page
- Show share links, embed codes, or content to copy
- Confirm non-destructive actions with additional context
When Not to Use
- Destructive or irreversible actions requiring explicit confirmation - use Alert Dialog
- Side panel content like navigation or filters - use Sheet
- Mobile-first bottom panels or action pickers - use Drawer
- Simple tooltips or popovers for contextual info - use Popover or Tooltip
Edit Profile
Dialog with form inputs and footer actions. The primary action is placed on the right.
Share Link
Dialog for sharing content with a copy action and read-only input.
Simple
Minimal dialog with only header content and no footer actions.
UX & Design Guidelines
Content Structure
Always include a DialogTitle and DialogDescription for clarity and accessibility. Keep titles concise (2-5 words) and descriptions actionable. Place form fields in the body area between header and footer. Use DialogFooter for action buttons, with the primary action on the right.
Sizing & Layout
The default max-width is sm:max-w-lg (512px). Use sm:max-w-[425px] for compact form dialogs or sm:max-w-md for medium content. On mobile, dialogs expand to full width with a 1rem margin on each side. Avoid dialogs taller than 85vh; use scrollable content areas for long forms.
Responsive Behavior
Dialog footer buttons stack vertically in reverse order on mobile via flex-col-reverse, placing the primary action at the bottom for easier thumb reach. On larger screens, buttons align horizontally to the right. Consider replacing dialogs with full-page views on very small screens for complex forms.
Animation & Transitions
Dialogs use coordinated enter/exit animations: the overlay fades in/out while the content panel fades and scales from 95% to 100%. The 200ms duration keeps transitions snappy without feeling abrupt. The backdrop uses bg-black/50 to maintain readability while dimming background content.
Accessibility
Keyboard Navigation
- Tab — Move focus between focusable elements within the dialog
- Shift + Tab — Move focus backward; focus is trapped within the dialog
- Escape — Close the dialog and return focus to the trigger element
- Enter or Space — Activate the trigger button to open the dialog
Focus Management
When the dialog opens, focus moves to the first focusable element inside. Focus is trapped within the dialog while open, preventing interaction with background content. When the dialog closes, focus is restored to the trigger element that opened it. Use autoFocus on a specific input to direct initial focus to the most relevant field.
Screen Reader Support
- The dialog is announced with
role="dialog"andaria-modal="true" DialogTitleprovides the accessible name viaaria-labelledbyDialogDescriptionprovides the accessible description viaaria-describedby- Background content is marked as inert, hidden from assistive technology
- The close button includes a
sr-only"Close" label for screen readers
Modal vs Non-Modal
By default, dialogs are modal: they trap focus, show an overlay, and mark background content as inert. Set modal={false} for non-modal dialogs where users can still interact with background content. Non-modal dialogs do not trap focus or render an overlay, which is useful for persistent panels or chat widgets.
ARIA Attributes
{/* Dialog with descriptive title and description */}
<Dialog>
<DialogTrigger asChild>
<Button>Edit Profile</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
{/* DialogTitle maps to aria-labelledby */}
<DialogTitle>Edit Profile</DialogTitle>
{/* DialogDescription maps to aria-describedby */}
<DialogDescription>
Update your display name and username.
</DialogDescription>
</DialogHeader>
{/* Form fields inside dialog */}
<form>
<Label htmlFor="name">Name</Label>
<Input id="name" />
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button type="submit">Save</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
{/* Non-modal dialog (no overlay, no focus trap) */}
<Dialog modal={false}>
<DialogTrigger asChild>
<Button variant="outline">Open Non-Modal</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Non-Modal Dialog</DialogTitle>
<DialogDescription>
Users can still interact with content behind this dialog.
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>Related Components
Alert Dialog
Modal dialog for destructive or important actions that require explicit user confirmation.
Sheet
Side panel that slides in from any edge, ideal for navigation, filters, or detail views.
Drawer
Bottom panel with drag-to-dismiss, optimized for mobile-first action sheets and pickers.
Popover
Floating panel anchored to a trigger element for contextual content without a full overlay.
API Reference
The Dialog component is composed of several sub-components. Below are the props for each.
Dialog
The root component that manages open state and context for all sub-components.
| Prop | Type | Default | Description |
|---|---|---|---|
| open | boolean | — | Controlled open state of the dialog. |
| onOpenChange | (open: boolean) => void | — | Event handler called when the open state changes. |
| defaultOpen | boolean | false | The initial open state when uncontrolled. |
| modal | boolean | true | When true, interaction with outside elements is disabled and only dialog content is visible to screen readers. |
DialogContent
The content panel rendered inside a portal with an overlay backdrop.
| Prop | Type | Default | Description |
|---|---|---|---|
| showCloseButton | boolean | true | Whether to render the close button in the top-right corner. |
| onEscapeKeyDown | (event: KeyboardEvent) => void | — | Event handler called when the Escape key is pressed. Can be prevented with event.preventDefault(). |
| onPointerDownOutside | (event: PointerDownOutsideEvent) => void | — | Event handler called when a pointer event occurs outside the dialog. Can be prevented with event.preventDefault(). |
| onInteractOutside | (event: Event) => void | — | Event handler called when any interaction occurs outside the dialog. |
| className | string | — | Additional CSS classes for the content container. |
DialogTrigger
The button that opens the dialog. Use asChild to render your own button component.
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | false | When true, the trigger renders its child as the root element, merging props and behavior. |
DialogClose
A button that closes the dialog. Use asChild to render your own button component.
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | false | When true, the close button renders its child as the root element, merging props and behavior. |