Skip to content
Components

Dialog

A modal window above a scrim, in five widths. Focus moves into it when it opens and goes back to the trigger when it closes. It uses shadow-high, the shadow kept for floating layers.

Preview

Props

  • open
  • defaultOpen
  • size
  • showCloseButton
  • title
import { Button } from "@/components/ui/button"
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"

<Dialog>
  <DialogTrigger asChild>
    <Button>Rename project</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Rename project</DialogTitle>
      <DialogDescription>Pick a name your team will recognize.</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="secondary">Cancel</Button>
      </DialogClose>
      <Button>Save</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

Usage

When to use

  • A short task that should block the page until it is done or cancelled, such as renaming a project.
  • A confirmation that is not destructive.
  • A task that needs room, such as a table or a preview, with a larger size.

When not to use

  • Confirming a destructive action. Use AlertDialog.
  • Long forms or reading. Give them their own page.
  • Opening a dialog from a dialog. Close the first one before the next one opens, or give the flow its own page.

Variants

size

Set on DialogContent. Widest the panel gets: sm 448px (short confirmations), md 512px (forms of a few fields), lg 672px, xl 896px (tables, previews), full the whole viewport minus spacing-lg on each side, in height too. On a narrow screen every size fills the width minus spacing-lg.

  • sm

  • mddefault

  • lg

  • xl

  • full

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

<Dialog>
  <DialogTrigger asChild>
    <Button>Rename project</Button>
  </DialogTrigger>
  <DialogContent size="sm">
    <DialogHeader>
      <DialogTitle>Rename project</DialogTitle>
      <DialogDescription>Pick a name your team will recognize.</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="secondary">Cancel</Button>
      </DialogClose>
      <Button>Save</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>
<Dialog>
  <DialogTrigger asChild>
    <Button>Rename project</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Rename project</DialogTitle>
      <DialogDescription>Pick a name your team will recognize.</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="secondary">Cancel</Button>
      </DialogClose>
      <Button>Save</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>
<Dialog>
  <DialogTrigger asChild>
    <Button>Rename project</Button>
  </DialogTrigger>
  <DialogContent size="lg">
    <DialogHeader>
      <DialogTitle>Rename project</DialogTitle>
      <DialogDescription>Pick a name your team will recognize.</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="secondary">Cancel</Button>
      </DialogClose>
      <Button>Save</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>
<Dialog>
  <DialogTrigger asChild>
    <Button>Rename project</Button>
  </DialogTrigger>
  <DialogContent size="xl">
    <DialogHeader>
      <DialogTitle>Rename project</DialogTitle>
      <DialogDescription>Pick a name your team will recognize.</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="secondary">Cancel</Button>
      </DialogClose>
      <Button>Save</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>
<Dialog>
  <DialogTrigger asChild>
    <Button>Rename project</Button>
  </DialogTrigger>
  <DialogContent size="full">
    <DialogHeader>
      <DialogTitle>Rename project</DialogTitle>
      <DialogDescription>Pick a name your team will recognize.</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="secondary">Cancel</Button>
      </DialogClose>
      <Button>Save</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

Examples

Default

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

<Dialog>
  <DialogTrigger asChild>
    <Button>Rename project</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Rename project</DialogTitle>
      <DialogDescription>Pick a name your team will recognize.</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="secondary">Cancel</Button>
      </DialogClose>
      <Button>Save</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

Do and don't

Do

  • Use a dialog for a short task or a confirmation that should block the page until done or cancelled.
  • Pick the smallest size that fits the content: sm for a sentence and two buttons, md for a short form, lg or xl for tables and previews. Long content scrolls inside the panel, which stays spacing-lg from the viewport edges.
  • Let Escape, the close button and a scrim click close it.
  • Open and close without animation (current behavior). If an animation is added later, reduced motion gets a fade only.
  • Return focus to the trigger on close.

Don't

  • Don't use Dialog to confirm a destructive action. Use AlertDialog, which focuses Cancel first and doesn't close on a scrim click.
  • Don't open a dialog from a dialog.
  • Don't use a dialog for long forms or reading; use a page or sheet. A bigger size does not make a long form short.
  • Don't use size full for a short task; the user loses sight of the page they came from.
  • Don't leave out DialogTitle. If it shouldn't show, make it sr-only.
  • Don't remove shadow-high from the panel. A dialog floats above the page, so a border alone isn't enough.

Props

NameTypeValuesDefaultDescription
openboolean--Set on Dialog. Whether the dialog is open, when you control it. Pair it with onOpenChange. Only on web.
defaultOpenboolean-falseSet on Dialog. Opens the dialog on first render, when you don't control it. Only on web.
onOpenChangefunction--Set on Dialog. Called with true or false when the dialog opens or closes (trigger, close button, Escape, scrim). Only on web.
modalboolean-trueSet on Dialog. true traps focus and hides the rest of the page from assistive tech. Keep it true; false only for a panel the user works next to, which is better as a sheet. Only on web.
sizeenumsm | md | lg | xl | fullmdSet on DialogContent. Widest the panel gets: sm 448px (short confirmations), md 512px (forms of a few fields), lg 672px, xl 896px (tables, previews), full the whole viewport minus spacing-lg on each side, in height too. On a narrow screen every size fills the width minus spacing-lg.
showCloseButtonboolean-trueDialogContent: show the corner close button. DialogFooter takes the same prop (default false) to add a secondary Close button. Only on web.

Slots

NameRequiredDescription
triggernoElement that opens the dialog.
titleyesDialog title (required for a11y).
descriptionnoOptional supporting text.
contentnoMain body.
footernoActions.

Anatomy

PartDescription
rootRoot. Controls open state.
overlayScrim behind the content. color-overlay-scrim.
contentThe floating panel: raised surface, radius-page, shadow-high, border.
headerTop block for title and description.
titleAccessible name of the dialog.
descriptionOptional supporting text.
footerActions row at the bottom.
closeIcon button that dismisses the dialog.
iconTabler XIcon inside the close control, 16px.
hitAreaTransparent ::before on the close control, never smaller than size-target-min.

Accessibility

Role: dialog

KeysAction
EscapeCloses the dialog and returns focus to the trigger.
TabCycles focus inside the dialog (focus trap).
  • Traps focus and closes on Escape. Hides the rest of the page from assistive tech (aria-hidden on siblings); it does not set aria-modal.
  • Always provide a DialogTitle (visible or sr-only) so the dialog has an accessible name.
  • The corner close is a DialogClose with an sr-only "Close" label and a Tabler XIcon. If showCloseButton is false on DialogContent, the footer must have a Cancel or Close.
  • Do not nest dialogs.

Install

npx shadcn add https://werewolf-ui.vercel.app/r/dialog.json

This also adds styles/werewolf.css, the tokens every component uses, and any other component it needs. Import that file once in your root CSS, after @import "tailwindcss", with a path relative to that CSS file (from app/globals.css it is @import "../styles/werewolf.css").