# Dialog

A window overlaid on the page.

Source: [dialog.tsx](https://github.com/jakejarvis/jarvis-ui/blob/main/src/components/ui/dialog.tsx)

```tsx
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { toast } from "@/components/ui/toast";

export default function DialogEditProfile() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="secondary" />}>Edit profile</DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Edit profile</DialogTitle>
          <DialogDescription>Changes are visible to everyone at Acme.</DialogDescription>
        </DialogHeader>
        <FieldGroup className="gap-3">
          <Field>
            <FieldLabel htmlFor="profile-name">Full name</FieldLabel>
            <Input id="profile-name" defaultValue="Ada Park" />
          </Field>
          <Field>
            <FieldLabel htmlFor="profile-title">Job title</FieldLabel>
            <Input id="profile-title" defaultValue="Product designer" />
          </Field>
        </FieldGroup>
        <DialogFooter>
          <DialogClose render={<Button variant="secondary" />}>Cancel</DialogClose>
          <DialogClose
            render={<Button variant="primary" />}
            onClick={() => toast.add({ title: "Profile updated", type: "success" })}
          >
            Save changes
          </DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
```

## Installation

Add the registry, once per project:

```bash
npx shadcn@latest registry add @jarvis=https://ui.jarv.is/r/{name}.json
```

Then add the component:

```bash
npx shadcn@latest add @jarvis/dialog
```

## Usage

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

export function RenameDialog() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="secondary" />}>Rename</DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Rename project</DialogTitle>
          <DialogDescription>The new name is visible to everyone.</DialogDescription>
        </DialogHeader>
        <p>…</p>
        <DialogFooter>
          <DialogClose render={<Button variant="secondary" />}>Cancel</DialogClose>
          <DialogClose render={<Button variant="primary" />}>Save</DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
```

Pass a `Button` to `render` on the trigger and close buttons. Always include a `DialogTitle`.

To open or close the dialog from your own code, control it with `open` and `onOpenChange`.

## Composition

- `Dialog`
  - `DialogTrigger`
  - `DialogContent`
    - `DialogHeader`
      - `DialogTitle`
      - `DialogDescription`
    - `DialogFooter`
      - `DialogClose`

## Examples

### Wide

Set a wider `max-w` on `DialogContent` for content that needs more room.

```tsx
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import {
  Field,
  FieldContent,
  FieldDescription,
  FieldLabel,
  FieldSeparator,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";

export default function DialogWide() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="secondary" />}>Project settings</DialogTrigger>
      <DialogContent className="sm:max-w-[640px]">
        <DialogHeader>
          <DialogTitle>Project settings</DialogTitle>
          <DialogDescription>
            Change this project's name, URL, description and visibility.
          </DialogDescription>
        </DialogHeader>
        <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
          <Field>
            <FieldLabel htmlFor="project-name">Name</FieldLabel>
            <Input id="project-name" defaultValue="Website redesign" />
          </Field>
          <Field>
            <FieldLabel htmlFor="project-slug">URL</FieldLabel>
            <Input id="project-slug" defaultValue="website-redesign" />
          </Field>
          <Field className="sm:col-span-2">
            <FieldLabel htmlFor="project-description">Description</FieldLabel>
            <Textarea
              id="project-description"
              defaultValue="Refresh the marketing site with the new brand."
            />
          </Field>
        </div>
        <FieldSeparator />
        <Field orientation="horizontal">
          <FieldContent>
            <FieldLabel htmlFor="project-public">Public project</FieldLabel>
            <FieldDescription>Anyone with the link can view tasks and files.</FieldDescription>
          </FieldContent>
          <Switch id="project-public" />
        </Field>
        <DialogFooter>
          <Button variant="destructive" className="sm:mr-auto">
            Delete project
          </Button>
          <DialogClose render={<Button variant="secondary" />}>Cancel</DialogClose>
          <DialogClose render={<Button variant="primary" />}>Save</DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
```

### Footer close button

`showCloseButton={false}` on `DialogContent` hides the close button in the corner, and
`showCloseButton` on `DialogFooter` adds one to the footer instead.

```tsx
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Kbd, KbdGroup } from "@/components/ui/kbd";

const shortcuts = [
  { label: "Search", keys: ["⌘", "K"] },
  { label: "New task", keys: ["C"] },
  { label: "Toggle sidebar", keys: ["⌘", "B"] },
];

export default function DialogFooterCloseButton() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="ghost" />}>Keyboard shortcuts</DialogTrigger>
      <DialogContent showCloseButton={false}>
        <DialogHeader>
          <DialogTitle>Keyboard shortcuts</DialogTitle>
        </DialogHeader>
        <div className="flex flex-col gap-2 text-sm">
          {shortcuts.map(({ label, keys }) => (
            <div key={label} className="flex items-center justify-between">
              <span className="text-muted-foreground">{label}</span>
              <KbdGroup>
                {keys.map((key) => (
                  <Kbd key={key}>{key}</Kbd>
                ))}
              </KbdGroup>
            </div>
          ))}
        </div>
        <DialogFooter showCloseButton />
      </DialogContent>
    </Dialog>
  );
}
```

## Props

`createDialogHandle` makes a handle to pass as `handle` to a `Dialog`. Its `open(null)` and
`close()` open and close the dialog from code, such as a keyboard shortcut, and `isOpen` says
whether it's open. A `DialogTrigger` outside the `Dialog` takes the same handle.

| Part | Extends |
| --- | --- |
| `Dialog` | [`Dialog.Root`](https://base-ui.com/react/components/dialog#root) |
| `DialogTrigger` | [`Dialog.Trigger`](https://base-ui.com/react/components/dialog#trigger) |
| `DialogPortal` | [`Dialog.Portal`](https://base-ui.com/react/components/dialog#portal) |
| `DialogClose` | [`Dialog.Close`](https://base-ui.com/react/components/dialog#close) |
| `DialogOverlay` | [`Dialog.Backdrop`](https://base-ui.com/react/components/dialog#backdrop) |
| `DialogContent` | [`Dialog.Popup`](https://base-ui.com/react/components/dialog#popup) |
| `DialogHeader` | `<div>` |
| `DialogFooter` | `<div>` |
| `DialogTitle` | [`Dialog.Title`](https://base-ui.com/react/components/dialog#title) |
| `DialogDescription` | [`Dialog.Description`](https://base-ui.com/react/components/dialog#description) |

The tables leave out `className`, `style` and `render`. Parts built on Base UI take them as its [styling](https://base-ui.com/react/handbook/styling) and [composition](https://base-ui.com/react/handbook/composition) guides describe.

### Dialog

| Prop                       | Type                                                                      | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| -------------------------- | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `open?`                    | `boolean`                                                                 | Whether the dialog is currently open.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `defaultOpen?`             | `boolean`                                                                 | Whether the dialog is initially open. To render a controlled dialog, use the `open` prop instead. Default: `false`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `modal?`                   | `"trap-focus" \| boolean`                                                 | Determines if the dialog enters a modal state when open. - `true`: user interaction is limited to just the dialog: focus is trapped, document page scroll is locked, and pointer interactions on outside elements are disabled. - `false`: user interaction with the rest of the document is allowed. - `'trap-focus'`: focus is trapped inside the dialog, but document page scroll is not locked and pointer interactions outside of it remain enabled. When `modal` is `true` or `'trap-focus'`, render `<Dialog.Close>` inside `<Dialog.Popup>` so touch screen readers can escape the popup. Default: `true` |
| `onOpenChange?`            | `((open: boolean, eventDetails: Dialog.Root.ChangeEventDetails) => void)` | Event handler called when the dialog is opened or closed.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `onOpenChangeComplete?`    | `((open: boolean) => void)`                                               | Event handler called after any animations complete when the dialog is opened or closed.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `disablePointerDismissal?` | `boolean`                                                                 | Whether to prevent the dialog from closing on outside presses. For non-modal dialogs, this also prevents the dialog from closing when focus moves outside of it. Default: `false`                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `actionsRef?`              | `React.RefObject<DialogRootActions \| null>`                              | A ref to imperative actions. - `unmount`: Ends the closing phase of the dialog after an externally controlled closing animation finishes. Call `preventUnmountOnClose()` in `onOpenChange` first, otherwise the dialog completes closing on its own. Whether it leaves the DOM is decided by `keepMounted` on the portal. - `close`: Closes the dialog imperatively when called.                                                                                                                                                                                                                                  |
| `handle?`                  | `Dialog.Handle<unknown>`                                                  | A handle to associate the dialog with a trigger. If specified, allows external triggers to control the dialog's open state. Can be created with the Dialog.createHandle() method.                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `children?`                | `PayloadChildRenderFunction<unknown> \| React.ReactNode`                  | The content of the dialog. This can be a regular React node or a render function that receives the `payload` of the active trigger.                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `triggerId?`               | `string \| null`                                                          | ID of the trigger that the dialog is associated with. This is useful in conjunction with the `open` prop to create a controlled dialog. There's no need to specify this prop when the dialog is uncontrolled (that is, when the `open` prop is not set).                                                                                                                                                                                                                                                                                                                                                          |
| `defaultTriggerId?`        | `string \| null`                                                          | ID of the trigger that the dialog is associated with. This is useful in conjunction with the `defaultOpen` prop to create an initially open dialog.                                                                                                                                                                                                                                                                                                                                                                                                                                                               |

### DialogTrigger

| Prop            | Type                     | Description                                                                                                                                                                                        |
| --------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `handle?`       | `Dialog.Handle<unknown>` | A handle to associate the trigger with a dialog. Can be created with the Dialog.createHandle() method.                                                                                             |
| `payload?`      | `unknown`                | A payload to pass to the dialog when it is opened.                                                                                                                                                 |
| `id?`           | `string`                 | ID of the trigger. In addition to being forwarded to the rendered element, it is also used to specify the active trigger for the dialog in controlled mode (with the DialogRoot `triggerId` prop). |
| `nativeButton?` | `boolean`                | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). Default: `true`  |

### DialogPortal

| Prop           | Type                                                                                      | Description                                                                               |
| -------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `keepMounted?` | `boolean`                                                                                 | Whether to keep the portal mounted in the DOM while the popup is hidden. Default: `false` |
| `container?`   | `HTMLElement \| React.RefObject<HTMLElement \| ShadowRoot \| null> \| ShadowRoot \| null` | A parent element to render the portal element into.                                       |

### DialogClose

| Prop            | Type      | Description                                                                                                                                                                                       |
| --------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `nativeButton?` | `boolean` | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). Default: `true` |

### DialogOverlay

| Prop           | Type      | Description                                                                 |
| -------------- | --------- | --------------------------------------------------------------------------- |
| `forceRender?` | `boolean` | Whether the backdrop is forced to render even when nested. Default: `false` |

### DialogContent

| Prop               | Type                                                                                                                          | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `initialFocus?`    | `boolean \| React.RefObject<HTMLElement \| null> \| ((openType: InteractionType) => boolean \| HTMLElement \| null \| void)`  | Determines the element to focus when the dialog is opened. By default, focus moves to the first tabbable element inside the popup, except when the dialog is opened by touch — then the popup itself is focused to avoid opening the virtual keyboard. - `false`: Do not move focus. - `true`: Move focus based on the default behavior (first tabbable element or popup). - `RefObject`: Move focus to the ref element. - `function`: Called with the interaction type (`mouse`, `touch`, `pen`, or `keyboard`). Return an element to focus, `true` to use the default behavior, `null` to fall back to the default behavior, or `false`/`undefined` to do nothing. |
| `finalFocus?`      | `boolean \| React.RefObject<HTMLElement \| null> \| ((closeType: InteractionType) => boolean \| HTMLElement \| null \| void)` | Determines the element to focus when the dialog is closed. - `false`: Do not move focus. - `true`: Move focus based on the default behavior (trigger or previously focused element). - `RefObject`: Move focus to the ref element. - `function`: Called with the interaction type (`mouse`, `touch`, `pen`, or `keyboard`). Return an element to focus, `true` to use the default behavior, `null` to fall back to the default behavior, or `false`/`undefined` to do nothing.                                                                                                                                                                                       |
| `showCloseButton?` | `boolean`                                                                                                                     | Default: `true`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |

### DialogFooter

| Prop               | Type      | Description      |
| ------------------ | --------- | ---------------- |
| `showCloseButton?` | `boolean` | Default: `false` |
