Skip to content
Protocore Design Systemv1.6.9

/// Overlay

Dialog

A modal panel over a dimmed backdrop for focused tasks — forms, confirmations, and detail views that demand attention.

import { Dialog } from "@protocore/pds";
View as Markdown

Basics

Compose the parts: Dialog.Root owns open state, Dialog.Trigger (via asChild) opens it, and Dialog.Content portals the backdrop + centered panel. A Title is required for accessibility; Dialog.Close dismisses.

With a form

Everything inside Dialog.Content is yours to compose — stack Fields and Inputs, then wrap each footer action in Dialog.Close asChild so a click closes the dialog. Focus is trapped until it does.

Controlled

Drive open / onOpenChange yourself to open the dialog from anywhere or to gate closing on an async result. Omit Dialog.Trigger entirely when you open it programmatically.

open = false

When to use it

A Dialog interrupts the flow for a self-contained task the user opts into: an edit form, a multi-step choice, a focused detail view. It dims the page, traps focus, and closes on Esc or backdrop click.

For a destructive or irreversible confirmation, use [AlertDialog](/overlay/alert-dialog) — or its ConfirmDialog convenience — which removes the backdrop-dismiss escape hatch so the choice is deliberate. For a long detail panel or side form that shouldn't cover the whole screen, use a [Sheet](/overlay/sheet). For lightweight, non-modal floating content anchored to a control, use a [Popover](/overlay/popover). Reserve the Dialog for moments that genuinely warrant blocking the rest of the UI.

Compound parts

Dialog is a compound: `Dialog.Root` (state) · `Dialog.Trigger` · `Dialog.Content` (backdrop + panel, renders the close × unless showClose={false}) · `Dialog.Title` (required) · `Dialog.Description` · `Dialog.Footer` (right-aligned action row) · `Dialog.Close`. Compose only the parts you need — but always include a Title, or pass aria-describedby={undefined} when you deliberately omit a Description.

Usage

Do

  • Always render a Dialog.Title — Radix warns without one and screen readers need it.
  • Wrap footer actions in Dialog.Close so the panel dismisses on click.
  • Put the primary action last (rightmost) in the footer, secondary before it.
  • Reach for ConfirmDialog when the task is a yes/no destructive confirmation.

Don't

  • Nest a Dialog inside another Dialog — redesign the flow instead.
  • Use a Dialog for an irreversible action's confirmation — use AlertDialog.
  • Cram a whole workflow in; if it scrolls the viewport, use a Sheet or a page.
  • Disable Esc / backdrop dismissal on a routine dialog — that's AlertDialog behavior.

Accessibility

KeysAction
EscCloses the dialog and returns focus to the trigger.
TabCycles focus forward, trapped within the panel.
Shift + TabCycles focus backward, trapped within the panel.
Enter / SpaceActivates the focused control (e.g. a footer button).
  • Focus is trapped inside the panel while open and restored to the trigger on close.
  • Opening the dialog moves focus into the panel; the close × is reachable and labelled.
  • The panel is role=dialog with aria-modal; Title labels it and Description describes it.
  • Content outside the dialog is inert and hidden from assistive tech while it's open.

Dialog.Content props

PropTypeDefaultDescription
asChildboolean
classNamestring
showClosebooleantrueRender a top-right 32px ghost × close button.
styleCSSProperties

Dialog.Footer props

PropTypeDefaultDescription
classNamestring
styleCSSProperties

Related

  • AlertDialogA modal that interrupts to confirm a consequential or destructive action — no backdrop escape, an explicit choice required.
  • SheetA full-height panel that slides in from the right for detail views and side forms that shouldn't cover the whole screen.
  • PopoverA non-modal floating panel anchored to a trigger — for secondary controls, filters, and rich detail that shouldn't block the page.
  • CommandPaletteA ⌘K spotlight finder — grouped, filterable commands driven entirely from the keyboard over a dimmed backdrop.