Overlays/Dialog

Dialog

A window overlaid on the primary window, rendering content.

Themed

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" and aria-modal="true"
  • DialogTitle provides the accessible name via aria-labelledby
  • DialogDescription provides the accessible description via aria-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>

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.

PropTypeDefaultDescription
openbooleanControlled open state of the dialog.
onOpenChange(open: boolean) => voidEvent handler called when the open state changes.
defaultOpenbooleanfalseThe initial open state when uncontrolled.
modalbooleantrueWhen 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.

PropTypeDefaultDescription
showCloseButtonbooleantrueWhether to render the close button in the top-right corner.
onEscapeKeyDown(event: KeyboardEvent) => voidEvent handler called when the Escape key is pressed. Can be prevented with event.preventDefault().
onPointerDownOutside(event: PointerDownOutsideEvent) => voidEvent handler called when a pointer event occurs outside the dialog. Can be prevented with event.preventDefault().
onInteractOutside(event: Event) => voidEvent handler called when any interaction occurs outside the dialog.
classNamestringAdditional CSS classes for the content container.

DialogTrigger

The button that opens the dialog. Use asChild to render your own button component.

PropTypeDefaultDescription
asChildbooleanfalseWhen 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.

PropTypeDefaultDescription
asChildbooleanfalseWhen true, the close button renders its child as the root element, merging props and behavior.