Dialog

A modal window that interrupts the current flow to capture user attention. Blocks background interaction, traps keyboard focus, and dismisses on ESC or clicking the overlay. Built on @base-ui/react/dialog.

Anatomy

<Dialog> is the stateful root. DialogTrigger opens it, DialogContent renders in a portal with an overlay. DialogHeader and DialogFooter are layout helpers. DialogClose dismisses from anywhere inside the content. The showCloseButton prop on DialogContent controls the built-in X button (default: true).

DialogTrigger → portalDialogHeader + DialogFootershowCloseButton (default: true)

Examples

Form dialog

Confirmation

No close button

Close Button

showCloseButton on DialogContent toggles the built-in X button in the header area. Disable it when the footer already provides a clear dismiss path.

showCloseButton={true} (default)
showCloseButton={false}

Design Guidelines

Do

  • Use for tasks needing a decision. Dialogs are best when the user must complete an action before returning — forms, confirmations, settings.
  • Keep dialogs focused. One task per dialog. If the content sprawls, consider a Sheet or a dedicated page instead.
  • Always provide a cancel path. Include a DialogClose or cancel button — never force the user to complete the action.

Don't

  • Don't nest dialogs. Opening a dialog from inside a dialog breaks focus management and confuses users.
  • Don't use for simple notifications. If no decision is needed, use a Toaster or inline Alert instead.
  • Don't block critical workflows. Avoid wrapping common actions in dialogs — reserve them for genuinely interruptive tasks.

Developer Reference

Accessibility

  • role="dialog" with aria-labelledby and aria-describedby wired to DialogTitle and DialogDescription.
  • Keyboard focus is trapped inside the open dialog — Tab cycles through focusable elements only.
  • ESC key and clicking the backdrop both dismiss the dialog.
  • DialogClose can wrap any element via asChild to create custom dismiss triggers.
  • Content renders into a portal — it is visually above the page but remains in the accessible tree.

Usage

import {
  Dialog,
  DialogTrigger,
  DialogContent,
  DialogHeader,
  DialogFooter,
  DialogTitle,
  DialogDescription,
  DialogClose,
} from "@/components/ui/dialog"

// Form dialog
<Dialog>
  <DialogTrigger asChild>
    <Button>Edit Profile</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Edit Profile</DialogTitle>
      <DialogDescription>Make changes and click save.</DialogDescription>
    </DialogHeader>
    <div className="space-y-4 py-2">
      <Input placeholder="Name" />
    </div>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="outline">Cancel</Button>
      </DialogClose>
      <Button>Save changes</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

// Without close button
<DialogContent showCloseButton={false}>...</DialogContent>

// Controlled
const [open, setOpen] = useState(false)
<Dialog open={open} onOpenChange={setOpen}>...</Dialog>