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

# Dialog - React Native

> The Dialog provides a labeled modal surface with configured content and actions.

> [!NOTE]
>
> For development, import `Dialog` from
> `@servicetitan/anvil2-rn`. For design, find it in the
> [Mobile Agentic Component Library file](https://www.figma.com/design/gjGWnAOnI1kukfzjsUQ6dP/Mobile-Agentic-Component-Library).

**Implementation**

```tsx theme={null}
import { Button, Dialog } from "@servicetitan/anvil2-rn";

function App() {
  return (
    <Dialog
      title="Leave without saving?"
      content="Your changes haven't been saved. Leaving now will discard them."
      primaryAction={{ label: "Leave" }}
      secondaryAction={{ label: "Stay" }}
      trigger={<Button label="Open dialog" />}
    />
  );
}
```

## Common Examples

To use the `Dialog`, pass the required `title` and `content`. Open it with an Anvil2 RN `Button` through `trigger`, or control it with `isOpen` and `onOpenChange`.

```tsx theme={null}
import { Button, Dialog } from "@servicetitan/anvil2-rn";

function ExampleComponent() {
  return (
    <Dialog
      title="Leave without saving?"
      trigger={<Button label="Leave page" />}
      content="Your changes haven't been saved."
      primaryAction={{ label: "Leave" }}
      secondaryAction={{ label: "Stay" }}
    />
  );
}
```

### Actions

Pass `primaryAction` for the confirm action and `secondaryAction` for dismissal. The dialog fixes both buttons at medium size and fixes the secondary action to the secondary variant. The dialog closes after each footer press; the button's own `onPress` fires first.

```tsx theme={null}
<Dialog
  title="You're all set"
  content="Your workspace is ready."
  primaryAction={{ label: "Got it" }}
  trigger={<Button label="Finish setup" />}
/>
```

Use `variant: "danger"` on `primaryAction` for destructive confirmation.

```tsx theme={null}
<Dialog
  title="Delete project?"
  content="This permanently deletes the project and everything in it."
  primaryAction={{ label: "Delete", variant: "danger" }}
  secondaryAction={{ label: "Cancel" }}
  trigger={<Button label="Delete project" variant="danger" />}
/>
```

Omit both actions for a footerless dialog. The header close still dismisses.

### Open state

When you pass `isOpen`, `onOpenChange` is required so the header close, scrim, Android back, and footer actions can request a close.

```tsx theme={null}
const [isOpen, setOpen] = useState(false);

<Dialog
  title="Leave without saving?"
  content="Your changes haven't been saved."
  isOpen={isOpen}
  onOpenChange={setOpen}
  primaryAction={{ label: "Leave" }}
/>
```

Set `isDismissable={false}` to keep the scrim and Android back from closing the dialog. The header close still dismisses.

## Accessibility

* The required `title` is a heading that names the modal. The dialog sets `accessibilityViewIsModal`.
* The header close control is a button named `"Close"` (English until the kit gains i18n).
* Long `content` scrolls inside the surface.

**Dialog Props**

```tsx theme={null}
<Dialog
  title="Leave without saving?"
  content="Your changes haven't been saved."
  primaryAction={{ label: "Leave" }}
  secondaryAction={{ label: "Stay" }}
  trigger={<Button label="Open dialog" />}
/>
```

## `Dialog` Props

The `Dialog` accepts the following props. It does not forward remaining React Native `Modal` props. Open state is a discriminated union: pass `isOpen` with `onOpenChange`, or omit `isOpen` for the uncontrolled form.

**content** `ReactNode` *required*

Body content. A plain string uses the dialog's body typography.

**title** `string` *required*

Accessible title rendered in the header.

**analyticsId** `string`

Opt-in telemetry id. When set under `Anvil2RNTelemetryProvider`, the
primary action emits `press`; the header close, scrim, and secondary
action emit `dismiss`.

**defaultOpen** `boolean`

Whether the dialog starts open when uncontrolled. Unavailable when
`isOpen` is set.

**isDismissable** `boolean` (default: true)

Whether tapping the scrim or using Android back closes the dialog.

**isOpen** `boolean`

Controlled open state. Pair with `onOpenChange`.

**onOpenChange** `(isOpen: boolean) =\> void`

Called when the open state changes. Required when `isOpen` is set.

**primaryAction** `DialogPrimaryAction`

Confirm action in the footer. Omit with `secondaryAction` for a
footerless dialog.

**secondaryAction** `Omit\<ButtonProps, "size" | "variant"\>`

Dismiss action in the footer, always secondary at medium size.

**testID** `string`

Test hook forwarded to the dialog surface.

**trigger** `ReactElement\<ButtonProps\>`

Anvil2 RN `Button` that opens the dialog. Omit for controlled state.

**DialogPrimaryAction**

```tsx theme={null}
const primaryAction: DialogPrimaryAction = {
  label: "Delete",
  variant: "danger",
  onPress: () => {},
};
```

## `DialogPrimaryAction`

Confirm-button props. `size` is fixed at `"medium"`. Remaining `Button` props such as `icon`, `onPress`, `isDisabled`, and `testID` stay available.

**label** `string` *required*

Visible label and accessible name for the confirm button.

**variant** `"primary" | "danger"` (default: primary)

Confirm-button intent. `"danger"` is for destructive confirmation.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.