{
  "title": "Dialog",
  "description": "Modal dialog with focus trap, scroll lock, escape-to-close, and click-outside.",
  "url": "/docs/components/dialog",
  "since": "0.2.0",
  "tags": [
    "overlay"
  ],
  "platform": "both",
  "source": "---\ntitle: Dialog\ndescription: Modal dialog with focus trap, scroll lock, escape-to-close, and click-outside.\nsince: 0.2.0\ntags: [overlay]\nplatform: both\ncategory: overlays\n---\n\nimport { BundleSize } from '@/components/bundle-size';\nimport { Preview } from '@/components/preview';\nimport { PropsTable } from '@/components/props-table';\n\n<BundleSize component=\"Dialog\" />\n\n## At a glance\n\n- Compose: `Dialog`, `DialogTrigger`, `DialogContent`, `DialogTitle`, `DialogDescription`, `DialogClose`, `DialogFooter`. Triggers and closes use `asChild` by default — wrap any element (Button, Link, custom Pressable) and it becomes the activator.\n- Cross-platform: uses RN `<Modal>` as the visibility primitive on every target. On web, additional effects layer on: focus trap, body scroll lock, Escape-to-close, click-outside-to-close, and focus restored to the trigger on close.\n- Accessibility: `role=\"dialog\"` with `aria-modal`, `aria-labelledby` on the title, `aria-describedby` on the description.\n\n## Preview\n\n<Preview name=\"dialog-basic\" />\n\n## Open state — `open`, `defaultOpen`, `onOpenChange`\n\n`open` (controlled) + `onOpenChange` is the parent-driven shape; pass\n`defaultOpen` instead for uncontrolled mode when the Dialog should\nmanage its own open state. Mixing the two prefers controlled and\nignores `defaultOpen`. The Trigger and Close subcomponents flip the\nstate for you, so most use sites don't need either prop.\n\n```tsx\n// Uncontrolled — Trigger / Close manage open state.\n<Dialog>\n    <Dialog.Trigger><Button>Open</Button></Dialog.Trigger>\n    <Dialog.Content>...</Dialog.Content>\n</Dialog>\n\n// Controlled — parent owns the state, useful for closing the dialog\n// after an async save completes.\nconst [open, setOpen] = useState(false);\n<Dialog open={open} onOpenChange={setOpen}>\n    <Dialog.Trigger><Button>Open</Button></Dialog.Trigger>\n    <Dialog.Content>...</Dialog.Content>\n</Dialog>\n```\n\n## Anatomy\n\n| Subcomponent | Role |\n|---|---|\n| `Dialog` | Root — owns open state (controlled or uncontrolled). |\n| `DialogTrigger` | Element that opens the dialog. `asChild` by default. |\n| `DialogContent` | The visible surface. Renders only while open. |\n| `DialogTitle` | Heading. Wires `aria-labelledby`. |\n| `DialogDescription` | Body subtitle. Wires `aria-describedby`. |\n| `DialogClose` | Element that closes the dialog. `asChild` by default; without children renders the canonical top-right ✕. |\n| `DialogFooter` | Right-aligned row for action buttons. |\n\n## Props\n\n### Dialog\n\n<PropsTable component=\"Dialog\" />\n\n### DialogTrigger\n\n<PropsTable component=\"DialogTrigger\" />\n\n### DialogContent\n\n<PropsTable component=\"DialogContent\" />\n\n### DialogClose\n\n<PropsTable component=\"DialogClose\" />\n"
}
