---
title: Dialog
description: >-
  A compound modal component for interruptive flows, with controlled state,
  focus management, and structured regions.
---

# Dialog

## Overview

Dialogs present focused, interruptive content above the main interface, typically requiring acknowledgment or a decision before continuing.

```tsx
<Dialog.Root>
	<Dialog.Trigger render={<Button label="Open dialog" priority="primary" />} />
	<Dialog.Content>
		<Dialog.Header
			title="Here's a sample dialog"
			description="It can include an optional description that provides a bit more context for the user. Isn't that nice?"
		/>
		<Dialog.Actions>
			<Button label="Confirm" priority="primary" />
			<Dialog.Close render={<Button label="Cancel" />} />
		</Dialog.Actions>
	</Dialog.Content>
</Dialog.Root>
```

## Usage Guidelines

- **Use for important interruptions:** Use a dialog when users need to stop and make a decision before continuing, such as confirming a destructive action or completing a short focused task. Avoid it for information that can stay in the page flow.
- **Keep content focused:** Limit dialog content to the context people need to make the decision or complete the task. Long forms, dense layouts, or content that requires extended browsing usually fit better in the page itself.
- **Make actions clear:** Include a clear primary action and a clear way to dismiss the dialog. Use action labels that describe the outcome, such as "Delete project" or "Save changes," rather than vague labels like "OK."

## API Reference

### `Dialog.Root`

Controls dialog state and provides context for all nested dialog parts.

| Prop           | Type                                                           | Default     |
| -------------- | -------------------------------------------------------------- | ----------- |
| `children`     | `ReactNode \| ((actions: { close: () => void }) => ReactNode)` | `undefined` |
| `open`         | `boolean`                                                      | `undefined` |
| `defaultOpen`  | `boolean`                                                      | `undefined` |
| `onOpenChange` | `(open: boolean) => void`                                      | `undefined` |

### `Dialog.Trigger`

Interactive element that opens the dialog when activated.

| Prop       | Type           | Default     |
| ---------- | -------------- | ----------- |
| `render`   | `ReactElement` | `undefined` |
| `disabled` | `boolean`      | `false`     |
| `children` | `ReactNode`    | `undefined` |

### `Dialog.Content`

Portal-rendered container for the dialog surface, backdrop, and viewport positioning.

| Prop           | Type                                                    | Default     |
| -------------- | ------------------------------------------------------- | ----------- |
| `children`     | `ReactNode`                                             | `undefined` |
| `className`    | `string`                                                | `undefined` |
| `initialFocus` | `RefObject<HTMLElement> \| (() => HTMLElement \| null)` | `undefined` |
| `finalFocus`   | `RefObject<HTMLElement> \| (() => HTMLElement \| null)` | `undefined` |

### `Dialog.Header`

Structured heading block that renders the dialog title and optional description.

| Prop          | Type     | Default     |
| ------------- | -------- | ----------- |
| `title`       | `string` | `undefined` |
| `description` | `string` | `undefined` |

### `Dialog.Body`

Main content region for dialog copy, fields, or custom layout.

| Prop       | Type        | Default     |
| ---------- | ----------- | ----------- |
| `children` | `ReactNode` | `undefined` |

### `Dialog.Actions`

Action row for primary and secondary controls, typically aligned to the end.

| Prop       | Type        | Default     |
| ---------- | ----------- | ----------- |
| `children` | `ReactNode` | `undefined` |

### `Dialog.Close`

Control that closes the dialog, often used for cancel or dismiss actions.

| Prop       | Type           | Default     |
| ---------- | -------------- | ----------- |
| `render`   | `ReactElement` | `undefined` |
| `children` | `ReactNode`    | `undefined` |
| `disabled` | `boolean`      | `false`     |

## Examples

### Confirmation

Use a dialog to confirm an action that interrupts the current flow, especially when the result is destructive or difficult to undo.

{/* TODO: use destructive buttons when ready */}

```tsx
<Dialog.Root>
	{({ close }) => (
		<>
			<Dialog.Trigger render={<Button label="Delete workspace" />} />
			<Dialog.Content>
				<Dialog.Header
					title="Delete workspace?"
					description="This removes all projects, drafts, and team access for this workspace. This action cannot be undone."
				/>
				<Dialog.Actions>
					<Button label="Delete workspace" priority="primary" onClick={() => close()} />
					<Dialog.Close render={<Button label="Keep workspace" />} />
				</Dialog.Actions>
			</Dialog.Content>
		</>
	)}
</Dialog.Root>
```

### Short tasks

Dialogs can hold short, focused tasks such as renaming an item or updating a single setting. Keep the content brief so the task stays easy to complete in place.

```tsx
<Dialog.Root>
	{({ close }) => (
		<>
			<Dialog.Trigger render={<Button label="Rename workspace" priority="primary" />} />
			<Dialog.Content>
				<Dialog.Header title="Update project" description="Changes will be reflected upon publish" />
				<Dialog.Body>
					<Stack gap={5}>
						<Field label="Title">
							<Input defaultValue="My Example Project" width="fluid" />
						</Field>
						<Field label="Description">
							<Input defaultValue="It's really cool" width="fluid" />
						</Field>
					</Stack>
				</Dialog.Body>
				<Dialog.Actions>
					<Button label="Save changes" priority="primary" onClick={() => close()} />
					<Dialog.Close render={<Button label="Cancel" />} />
				</Dialog.Actions>
			</Dialog.Content>
		</>
	)}
</Dialog.Root>
```

### Controlled state

Use the controlled API when dialog visibility depends on application state instead of on a user-initiated trigger, such as part of a larger workflow.

```tsx
const [open, setOpen] = useState(false);

<Dialog.Root open={open} onOpenChange={setOpen}>
	{({ close }) => (
		<>
			<Button label="Open controlled dialog" priority="primary" onClick={() => setOpen(true)} />
			<Dialog.Content>
				<Dialog.Header
					title="Publish changes?"
					description="This dialog is controlled by external state instead of relying only on the trigger."
				/>
				<Dialog.Actions>
					<Button label="Publish" priority="primary" onClick={() => close()} />
					<Dialog.Close render={<Button label="Cancel" />} />
				</Dialog.Actions>
			</Dialog.Content>
		</>
	)}
</Dialog.Root>
```

