build69
build69  /  Components  /  Overlays  /  Dialog

Dialog

v1.0.18Updated 20 Oct 2026

Modal dialog (Radix). Blocking overlay for confirmations, forms, or focused tasks. Ships with tonal headers, a confirm pattern, multi-step stepper, and form-dialog layout for richer flows.

Preview

Live · 14 stories
DefaultOpen ↗
▸Why this is recommendedmodaloverlaypopupradix+4

The default, out-of-the-box Dialog.

Recommended for+14
  • ✓Confirmation prompts
  • ✓Short forms
  • ✓Important alerts
  • ✓Destructive confirms
  • ✓Multi-step wizards
  • ✓Form dialogs
Tags+6
modaloverlaypopupradixconfirmwizardstepperdefault
Features+4
  • Focus trap
  • Escape to close
  • Composable header / footer
  • Tonal header on the shared Tone vocabulary (neutral / primary / success / warning / danger / info) — the same words Button, Badge and Alert take
  • Deprecated tone spellings still render: destructive is danger, default is neutral
  • Tolerates a bad tone or size: an unrecognised value falls back to the neutral tone / md size instead of throwing and blanking the page
  • Confirm pattern with primary danger action
  • Stepper wizard variant
  • Form dialog variant
  • Loading state on primary button
  • Composition safety: dialog parts (DialogHeader / DialogBody / DialogFooter) passed to the root without a DialogContent are wrapped in one, so a controlled-only dialog is a real modal instead of inline page content; a development warning names the repair
  • i18n: labels prop on the Dialog root overrides the built-in Close/Cancel strings (close aria-label, sr-only close text, footer Close button, confirm-footer Cancel fallback); merged over DEFAULT_DIALOG_LABELS
  • closeOnOutsideClick={false} keeps a form-holding dialog open when the overlay is clicked, while the ✕, the footer buttons and Escape all still close it — unlike closable={false}, which removes those too. Works in compositional mode as well and does not trigger Pro mode (fb_8e8e3cf7)
Not recommended for−2
  • ✕Slide-in panels (use Sheet)
  • ✕Tooltips (use Tooltip)
  • ✕Toasts (use Sonner)

Install

Ask your agent, or call the MCP tool directly. The resolved bundle is written into your project.

get_registry_item({ name: "Dialog" })

Usage

import { Dialog } from '@/components/overlays/dialog';

<Dialog open={open} onOpenChange={setOpen} tone="danger" title="Delete invoice?" confirmLabel="Delete" onConfirm={remove} />

Tick variants in the left menu — each adds a tab with that variant’s real story source, the same code the preview above renders.

Props

PropTypeRequired
open

Controlled open state

boolean—
defaultOpen

Initial open state for uncontrolled usage (forwarded to the Radix root; ignored when `open` is provided)

boolean—
onOpenChange

Open state callback

function—
trigger

The element that opens the dialog, rendered inside a Radix Trigger with asChild — so pass one element that accepts a ref, e.g. a Button. Supplying it switches the dialog into Pro mode; a compositional dialog uses <DialogTrigger> instead

React.ReactNode—
closable

Whether the dialog offers the usual ways out (default true). `false` removes the ✕ button entirely AND blocks Escape, for a dialog the user must answer — and it switches the dialog into Pro mode, so a compositional <DialogContent> cannot reach it. That is the difference from `closeOnOutsideClick`, which only governs the overlay click; when both are set, `closable={false}` wins and nothing but your own buttons closes the dialog

boolean—
confirm

Declarative confirm footer: { confirmLabel, onConfirm, cancelLabel?, onCancel?, destructive? }. Renders Cancel + a primary action, gives the content role="alertdialog" instead of "dialog", and moves opening focus to Cancel — the least destructive choice. `destructive` defaults to the dialog tone being danger. `onConfirm` may return a Promise but the dialog does not await it — drive the spinner yourself with `loading`, which disables the confirm button and puts a spinner in it

DialogConfirm—
steps

Wizard stepper rendered above the content, one entry per step: { id, label }. An empty array renders nothing

DialogStep[]—
activeStepId

Which `steps` entry is current, matched by id. Left out, the FIRST step is shown as active — an unknown id marks none of them, so keep it in step with the ids you passed

string—
tone

Header tone, on the catalog-wide Tone vocabulary. The old spellings still work and render identically: destructive is a deprecated alias of danger, default a deprecated alias of neutral. An unrecognised value falls back to neutral rather than throwing

'neutral' | 'primary' | 'success' | 'warning' | 'danger' | 'info'—
size

Panel width. An unrecognised value falls back to md

'sm' | 'md' | 'lg' | 'xl'—
loading

Primary action loading state

boolean—
labels

Override the built-in Close/Cancel strings (i18n). Merged over DEFAULT_DIALOG_LABELS; works in compositional and Pro modes and does not trigger Pro mode

Partial<DialogLabels>—
closeOnOutsideClick

Whether a click on the dimmed overlay closes the dialog (default true — the behaviour every dialog has always had). Set false for a dialog holding a form: a stray overlay click loses a half-filled invoice and nothing about the gesture says "discard my work". The ✕, the footer buttons and Escape all still close it, which is the difference from closable={false} — that one hides the ✕, blocks Escape and switches the dialog into Pro mode. This prop does neither and reaches a compositional DialogContent too (fb_8e8e3cf7)

boolean—

Every tonal component shares the same tone × emphasis API, so the pair looks identical across Button, Badge, Alert and more.

Related