> ## Documentation Index
> Fetch the complete documentation index at: https://docs.twenty.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Dialog

> Present a focused task above the current page.

export const StoryEmbed = ({storyId, title, height = 240}) => <>
    <Tabs>
      <Tab title="Light">
        <iframe title={`${title} (light)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:light`} width="100%" height={height} loading="lazy" style={{
  border: 0
}} />
      </Tab>
      <Tab title="Dark">
        <iframe title={`${title} (dark)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:dark`} width="100%" height={height} loading="lazy" style={{
  border: 0
}} />
      </Tab>
    </Tabs>
    <a href={`https://storybook.twenty.com/?path=/story/${storyId}`}>
      Open in Storybook
    </a>
  </>;

Use `Dialog` for forms, details, and other tasks that open above the page. It manages focus, keyboard dismissal, outside clicks, and scrolling. Use [AlertDialog](/ui/primitives/surfaces/alert-dialog) when the user must explicitly respond to a decision.

<StoryEmbed storyId="ui-surfaces-dialog--documentation" title="Dialog example" height={440} />

## Anatomy

```text theme={null}
Dialog.Root
├── Dialog.Trigger
└── Dialog.Popup
    ├── Dialog.Header
    │   ├── Dialog.Title
    │   └── Dialog.Description
    ├── Dialog.Body
    └── Dialog.Footer
        └── Dialog.Close
```

| Part                       | Requirement | Purpose                                                                        |
| -------------------------- | ----------- | ------------------------------------------------------------------------------ |
| `Root`                     | Required    | Coordinates visibility and modal behavior.                                     |
| `Trigger`                  | Optional    | Opens the dialog and receives focus when it closes.                            |
| `Popup`                    | Required    | Contains the dialog and includes its portal, backdrop, and viewport.           |
| `Title`                    | Recommended | Provides the accessible name. Supply `aria-label` on `Popup` when omitting it. |
| `Description`              | Optional    | Associates supporting text with the dialog.                                    |
| `Header`, `Body`, `Footer` | Optional    | Arrange headings, content, and actions.                                        |
| `Close`                    | Recommended | Provides a visible close action, including for touch screen readers.           |

## Uncontrolled state

`Root` owns visibility by default. Set `defaultOpen` for its initial state. Triggers, close controls, Escape, and outside clicks update that state.

```tsx theme={null}
import { Button } from 'twenty-ui/primitives/input';
import { Dialog } from 'twenty-ui/primitives/surfaces';

export const AccountDialog = () => (
  <Dialog.Root defaultOpen={false}>
    <Dialog.Trigger
      render={<Button>Account details</Button>}
    />
    <Dialog.Popup>
      <Dialog.Header>
        <Dialog.Title>Account details</Dialog.Title>
        <Dialog.Description>
          Review your account information.
        </Dialog.Description>
      </Dialog.Header>
      <Dialog.Body>Acme</Dialog.Body>
      <Dialog.Footer>
        <Dialog.Close render={<Button>Close</Button>} />
      </Dialog.Footer>
    </Dialog.Popup>
  </Dialog.Root>
);
```

## Controlled state

Pass `open` and `onOpenChange` when the application owns visibility. The callback receives the requested state and event details. Update state to accept a change, or call `eventDetails.cancel()` to reject it.

```tsx theme={null}
import { useState } from 'react';
import { Button } from 'twenty-ui/primitives/input';
import { Dialog } from 'twenty-ui/primitives/surfaces';

export const AccountDialog = () => {
  const [open, setOpen] = useState(false);

  return (
    <Dialog.Root open={open} onOpenChange={setOpen}>
      <Dialog.Trigger
        render={<Button>Account details</Button>}
      />
      <Dialog.Popup>
        <Dialog.Header>
          <Dialog.Title>Account details</Dialog.Title>
          <Dialog.Description>
            Review your account information.
          </Dialog.Description>
        </Dialog.Header>
        <Dialog.Body>Acme</Dialog.Body>
        <Dialog.Footer>
          <Dialog.Close render={<Button>Close</Button>} />
        </Dialog.Footer>
      </Dialog.Popup>
    </Dialog.Root>
  );
};
```

For an asynchronous form, keep the dialog open while submitting. Close it after success and display failures inside the dialog.

## Composition

`Trigger` and `Close` accept `render` to compose with `Button`, as shown above. Custom wrappers must [forward supplied props and refs](/ui/primitives/overview#custom-components). Layout parts also support `render`, native attributes, refs, styles, and class names. `Popup`, `Title`, and `Description` accept state-aware class names and styles.

Nest another `Dialog.Root` inside a popup to open a second dialog. Closing the nested dialog returns focus to its trigger in the parent.

To open one dialog from triggers outside `Root`, create a handle once with `Dialog.createHandle()` and pass it to `handle` on both `Root` and each `Trigger`. Trigger `payload` values are available in `Root`'s render function.

`Title` uses `Heading` and defaults to an `h2` with large type, primary text color, centered alignment, and spacing-4 below it. Use `level` to change the heading level independently of `size`, or `color` to change its text color. Native `className`, `style`, refs, and `render` are forwarded to the heading. Keep a heading element when using `render`.

## Focus, scrolling, and dismissal

`Root.modal` controls the interaction boundary:

| Value            | Focus                     | Page scrolling and outside interaction                   |
| ---------------- | ------------------------- | -------------------------------------------------------- |
| `true` (default) | Trapped inside the dialog | Page scrolling and outside interaction are blocked.      |
| `"trap-focus"`   | Trapped inside the dialog | Page scrolling and outside interaction remain available. |
| `false`          | Can leave the dialog      | Page scrolling and outside interaction remain available. |

Set `Popup.backdrop={false}` when outside content should remain visible and interactive. Set `Popup.initialFocus` or `Popup.finalFocus` to a ref or callback to choose focus targets; `false` disables that automatic focus move.

`Root.disablePointerDismissal` prevents outside clicks from requesting closure. To prevent Escape dismissal, cancel the `escape-key` change in `onOpenChange`. Always provide a visible way to close a modal dialog.

## Sizes and portals

`Popup.size` accepts `sm`, `md`, `lg`, `xl`, and `fullscreen`, and defaults to `md`. Long content scrolls within the popup. Width is constrained to the viewport.

Portals use the nearest theme container by default and preserve text direction. Set `container` to an element or ref to choose another destination; `null` defers mounting until a destination is available. `keepMounted` retains content and form state while closed.

Use `backdrop` props to customize the backdrop, and `viewportProps` for native attributes, styles, or `render` composition on the surrounding viewport. These props let application layout containers host a dialog while keeping the popup's semantics.

## Props

### Dialog.Root

<ParamField body="Root.actionsRef" type="RefObject<DialogRootActions | null>">
  A ref to imperative actions.

  * `unmount`: Manually unmounts the dialog.
    Call this after any externally controlled closing animation finishes.
  * `close`: Closes the dialog imperatively when called.
</ParamField>

<ParamField body="Root.children" type="ReactNode | PayloadChildRenderFunction<Payload>">
  The content of the dialog.
  This can be a regular React node or a render function that receives the `payload` of the active trigger.
</ParamField>

<ParamField body="Root.defaultOpen" type="boolean" default="false">
  Whether the dialog is initially open.

  To render a controlled dialog, use the `open` prop instead.
</ParamField>

<ParamField body="Root.defaultTriggerId" type="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.
</ParamField>

<ParamField body="Root.disablePointerDismissal" type="boolean" default="false">
  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.
</ParamField>

<ParamField body="Root.handle" type="DialogHandle<Payload>">
  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.
</ParamField>

<ParamField body="Root.modal" type="boolean | &#x22;trap-focus&#x22;" default="true">
  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.
</ParamField>

<ParamField body="Root.onOpenChange" type="((open: boolean, eventDetails: DialogRootChangeEventDetails) => void)">
  Event handler called when the dialog is opened or closed.
</ParamField>

<ParamField body="Root.onOpenChangeComplete" type="((open: boolean) => void)">
  Event handler called after any animations complete when the dialog is opened or closed.
</ParamField>

<ParamField body="Root.open" type="boolean">
  Whether the dialog is currently open.
</ParamField>

<ParamField body="Root.triggerId" type="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).
</ParamField>

### Dialog.Trigger

<ParamField body="Trigger.className" type="string | ((state: DialogTriggerState) => string | undefined)">
  CSS class applied to the element, or a function that
  returns a class based on the component's state.
</ParamField>

<ParamField body="Trigger.handle" type="DialogHandle<Payload>">
  A handle to associate the trigger with a dialog.
  Can be created with the Dialog.createHandle() method.
</ParamField>

<ParamField body="Trigger.id" type="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).
</ParamField>

<ParamField body="Trigger.nativeButton" type="boolean" default="true">
  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>`).
</ParamField>

<ParamField body="Trigger.payload" type="Payload">
  A payload to pass to the dialog when it is opened.
</ParamField>

<ParamField body="Trigger.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogTriggerState>">
  Allows you to replace the component's HTML element
  with a different tag, or compose it with another component.

  Accepts a `ReactElement` or a function that returns the element to render.
</ParamField>

<ParamField body="Trigger.style" type="CSSProperties | ((state: DialogTriggerState) => CSSProperties | undefined)">
  Style applied to the element, or a function that
  returns a style object based on the component's state.
</ParamField>

### Dialog.Popup

<ParamField body="Popup.backdrop" type="boolean | DialogBackdropProps" default="true" />

<ParamField body="Popup.className" type="string | ((state: DialogPopupState) => string | undefined)">
  CSS class applied to the element, or a function that
  returns a class based on the component's state.
</ParamField>

<ParamField body="Popup.container" type="HTMLElement | ShadowRoot | RefObject<HTMLElement | ShadowRoot | null> | null" />

<ParamField body="Popup.finalFocus" type="boolean | RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null)">
  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.
</ParamField>

<ParamField body="Popup.initialFocus" type="boolean | RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null)">
  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.
</ParamField>

<ParamField body="Popup.keepMounted" type="boolean" />

<ParamField body="Popup.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogPopupState>">
  Allows you to replace the component's HTML element
  with a different tag, or compose it with another component.

  Accepts a `ReactElement` or a function that returns the element to render.
</ParamField>

<ParamField body="Popup.size" type="&#x22;fullscreen&#x22; | &#x22;lg&#x22; | &#x22;md&#x22; | &#x22;sm&#x22; | &#x22;xl&#x22;" default="md" />

<ParamField body="Popup.style" type="CSSProperties | ((state: DialogPopupState) => CSSProperties | undefined)">
  Style applied to the element, or a function that
  returns a style object based on the component's state.
</ParamField>

<ParamField body="Popup.viewportProps" type="DialogViewportProps" />

### Dialog.Title

<ParamField body="Title.className" type="string | ((state: DialogTitleState) => string | undefined)">
  CSS class applied to the element, or a function that
  returns a class based on the component's state.
</ParamField>

<ParamField body="Title.color" type="&#x22;primary&#x22; | &#x22;secondary&#x22; | &#x22;tertiary&#x22;" default="primary">
  Theme font color. Defaults to `primary`.
</ParamField>

<ParamField body="Title.level" type="1 | 2 | 3 | 4 | 5 | 6" default="2">
  Semantic heading level, from `h1` through `h6`. Does not change the visual size. Defaults to `2`.
</ParamField>

<ParamField body="Title.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogTitleState>">
  Allows you to replace the component's HTML element
  with a different tag, or compose it with another component.

  Accepts a `ReactElement` or a function that returns the element to render.
</ParamField>

<ParamField body="Title.size" type="&#x22;lg&#x22; | &#x22;md&#x22; | &#x22;sm&#x22; | &#x22;xs&#x22;" default="lg">
  Visual font size, independent of the heading level. Defaults to `lg`.
</ParamField>

<ParamField body="Title.style" type="CSSProperties | ((state: DialogTitleState) => CSSProperties | undefined)">
  Style applied to the element, or a function that
  returns a style object based on the component's state.
</ParamField>

### Dialog.Description

<ParamField body="Description.className" type="string | ((state: DialogDescriptionState) => string | undefined)">
  CSS class applied to the element, or a function that
  returns a class based on the component's state.
</ParamField>

<ParamField body="Description.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogDescriptionState>">
  Allows you to replace the component's HTML element
  with a different tag, or compose it with another component.

  Accepts a `ReactElement` or a function that returns the element to render.
</ParamField>

<ParamField body="Description.style" type="CSSProperties | ((state: DialogDescriptionState) => CSSProperties | undefined)">
  Style applied to the element, or a function that
  returns a style object based on the component's state.
</ParamField>

### Dialog.Close

<ParamField body="Close.className" type="string | ((state: DialogCloseState) => string | undefined)">
  CSS class applied to the element, or a function that
  returns a class based on the component's state.
</ParamField>

<ParamField body="Close.nativeButton" type="boolean" default="true">
  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>`).
</ParamField>

<ParamField body="Close.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogCloseState>">
  Allows you to replace the component's HTML element
  with a different tag, or compose it with another component.

  Accepts a `ReactElement` or a function that returns the element to render.
</ParamField>

<ParamField body="Close.style" type="CSSProperties | ((state: DialogCloseState) => CSSProperties | undefined)">
  Style applied to the element, or a function that
  returns a style object based on the component's state.
</ParamField>

### Dialog.Header

<ParamField body="Header.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>">
  Allows you to replace the component's HTML element
  with a different tag, or compose it with another component.

  Accepts a `ReactElement` or a function that returns the element to render.
</ParamField>

### Dialog.Body

<ParamField body="Body.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>">
  Allows you to replace the component's HTML element
  with a different tag, or compose it with another component.

  Accepts a `ReactElement` or a function that returns the element to render.
</ParamField>

### Dialog.Footer

<ParamField body="Footer.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>">
  Allows you to replace the component's HTML element
  with a different tag, or compose it with another component.

  Accepts a `ReactElement` or a function that returns the element to render.
</ParamField>
