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

# Dialog

> A modal dialog, a centred card on desktop and a bottom sheet on mobile, built from DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogBody and DialogFooter.

```tsx theme={null}
import {
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogOverlay,
  DialogTitle,
  DialogTrigger,
} from "@fanvue/ui";
import type {
  DialogBodyProps,
  DialogCloseProps,
  DialogContentProps,
  DialogDescriptionProps,
  DialogFooterProps,
  DialogHeaderProps,
  DialogOverlayProps,
  DialogProps,
  DialogTitleProps,
  DialogTriggerProps,
} from "@fanvue/ui";
```

## Examples

## Default

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-dialog--default&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="440" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="Dialog — Default" />
</Frame>

```tsx theme={null}
import {
  Button,
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
  TextArea,
} from "@fanvue/ui";

<Dialog>
  <DialogTrigger asChild>
    <Button>Open Dialog</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Text</DialogTitle>
    </DialogHeader>
    <DialogBody>
      <TextArea placeholder="Type something…" fullWidth />
    </DialogBody>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="secondary">Cancel</Button>
      </DialogClose>
      <Button>Accept</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>
```

## Large (600px)

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-dialog--large-dialog&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="440" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="Dialog — Large (600px)" />
</Frame>

```tsx theme={null}
import {
  Button,
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@fanvue/ui";

<Dialog>
  <DialogTrigger asChild>
    <Button>Open Dialog</Button>
  </DialogTrigger>
  <DialogContent size="lg">
    <DialogHeader>
      <DialogTitle>Team Roles</DialogTitle>
    </DialogHeader>
    <DialogBody>
      <DialogDescription>Assign roles to team members to manage access.</DialogDescription>
      <div className="mt-4 space-y-3">
        {["Owner", "Team Manager", "Chatter"].map((role) => (
          <div
            key={role}
            className="flex items-center justify-between rounded-sm border border-neutral-alphas-200 p-4"
          >
            <span className="typography-body-default-16px-semibold text-content-primary">
              {role}
            </span>
            <span className="typography-body-small-14px-regular text-content-secondary">
              Permissions
            </span>
          </div>
        ))}
      </div>
    </DialogBody>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="secondary">Cancel</Button>
      </DialogClose>
      <Button>Save</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>
```

## With Header Description

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-dialog--with-header-description&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="440" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="Dialog — With Header Description" />
</Frame>

```tsx theme={null}
import {
  Button,
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@fanvue/ui";

<Dialog>
  <DialogTrigger asChild>
    <Button>Open Dialog</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Assign creator</DialogTitle>
      <DialogDescription>Choose a creator to assign to this account.</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="secondary">Cancel</Button>
      </DialogClose>
      <Button>Assign</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>
```

## Simple Default

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-dialog--simple-default&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="440" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="Dialog — Simple Default" />
</Frame>

```tsx theme={null}
import {
  Button,
  CheckIcon,
  Dialog,
  DialogBody,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@fanvue/ui";

<Dialog>
  <DialogTrigger asChild>
    <Button>Open Dialog</Button>
  </DialogTrigger>
  <DialogContent size="md">
    <DialogHeader showClose={false}>
      <DialogTitle>Your funds are on the way!</DialogTitle>
    </DialogHeader>
    <DialogBody>
      <p className="typography-body-default-16px-regular text-content-secondary">Remember:</p>
      <ul className="mt-3 space-y-2">
        <li className="typography-body-default-16px-regular flex items-start gap-2 text-content-secondary">
          <CheckIcon className="mt-0.5 size-5 shrink-0 text-content-primary" />
          <span>
            It could take up to{" "}
            <strong className="text-content-primary">10 working days</strong>
          </span>
        </li>
        <li className="typography-body-default-16px-regular flex items-start gap-2 text-content-secondary">
          <CheckIcon className="mt-0.5 size-5 shrink-0 text-content-primary" />
          <span>Your provider could charge you a fee</span>
        </li>
      </ul>
    </DialogBody>
    <DialogFooter>
      <Button>Got it</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>
```

## With Trigger

<Frame>
  <iframe src={"https://main--697a1b6dd4dad73ee9c0e5f5.chromatic.com/iframe.html?id=components-dialog--with-trigger&viewMode=story&shortcuts=false&singleStory=true&globals=theme:light"} width="100%" height="440" style={{border: "none", borderRadius: "8px"}} loading="lazy" title="Dialog — With Trigger" />
</Frame>

```tsx theme={null}
import {
  Button,
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@fanvue/ui";

<Dialog>
  <DialogTrigger asChild>
    <Button>Open Dialog</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Dialog Title</DialogTitle>
    </DialogHeader>
    <DialogBody>
      <DialogDescription>
        Dialog body text goes here. Describe the content or provide information to the user.
      </DialogDescription>
    </DialogBody>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="secondary">Cancel</Button>
      </DialogClose>
      <Button>Accept</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>
```

## Props

## Dialog

Root component that manages open/close state for a dialog.

A direct re-export of the Radix `DialogPrimitive.Root` primitive. It accepts all of that primitive's props, unchanged.

## DialogBody

Scrollable content area (slot) between the header and footer. Grows to fill available space and scrolls when content overflows.

Takes no props of its own. Accepts all standard `div` attributes, including `className`.

## DialogClose

Convenience alias for Radix `Dialog.Close`. Closes the dialog when clicked.

A direct re-export of the Radix `DialogPrimitive.Close` primitive. It accepts all of that primitive's props, unchanged.

## DialogContent

The dialog panel. Includes the overlay by default and portals to `document.body` by default.

Set `portal={false}` to keep overlay and content in the DOM subtree of the parent `Dialog`. `fixed` positioning still applies; ancestors with `transform` or `overflow` may affect layout.

On mobile viewports (\<640px), the dialog slides up from the bottom as a sheet with top-only border radius by default; pass `mobilePresentation="card"` to render a centered floating card instead (used for small confirmation dialogs). On larger viewports it renders centered with full border radius.

<ParamField path="asChild" type="boolean" />

<ParamField path="mobilePresentation" type="&#x22;sheet&#x22; | &#x22;card&#x22;" default="sheet">
  How the dialog presents below the `sm` breakpoint.

  * `"sheet"` — bottom sheet pinned to the viewport bottom edge (default)
  * `"card"` — centered floating card per the v2-modal confirmation spec: 16px side margins, 24px padding, 32px radius on all corners, no pull handle
</ParamField>

<ParamField path="overlay" type="boolean" default="true">
  When true, renders overlay automatically.
</ParamField>

<ParamField path="overlayProps" type="DialogOverlayProps">
  Props forwarded to the default `DialogOverlay` when `overlay` is `true`.
</ParamField>

<ParamField path="portal" type="boolean" default="true">
  When true, teleports overlay and panel to `document.body`. When false, renders inline in the React tree (useful inside theme providers or scoped containers).
</ParamField>

<ParamField path="showMobileHandle" type="boolean" default="true">
  Show the v2 mobile sheet pull handle. Only rendered when `mobilePresentation` is `"sheet"`.
</ParamField>

<ParamField path="size" type="&#x22;md&#x22; | &#x22;sm&#x22; | &#x22;lg&#x22;" default="md">
  Width preset for the dialog.

  * `"sm"` — 400px max-width (confirmations, simple forms)
  * `"md"` — 440px max-width (default, standard dialogs)
  * `"lg"` — 600px max-width (complex content, tables)
</ParamField>

## DialogDescription

Accessible description for the dialog. Rendered as secondary text.

<ParamField path="asChild" type="boolean" />

## DialogFooter

Footer bar for the dialog. Typically contains action buttons. Children are laid out in a horizontal row with equal flex-basis.

Takes no props of its own. Accepts all standard `div` attributes, including `className`.

## DialogHeader

Header bar for the dialog. Renders the title with an optional back arrow and close button.

<ParamField path="backLabel" type="string" default="Go back">
  Accessible label for the back button.
</ParamField>

<ParamField path="closeLabel" type="string" default="Close">
  Accessible label for the close button.
</ParamField>

<ParamField path="onBack" type="(() => void)">
  Called when the back button is clicked.
</ParamField>

<ParamField path="showBack" type="boolean">
  Show a back arrow button on the left side. Defaults to `true` when `onBack` is provided.
</ParamField>

<ParamField path="showClose" type="boolean" default="true">
  Show the close (X) button in the header.
</ParamField>

## DialogOverlay

Semi-transparent backdrop rendered behind the dialog content. Rendered by `DialogContent`; portaled to `document.body` when `DialogContent` `portal` is true.

<ParamField path="asChild" type="boolean" />

## DialogTitle

Accessible title for the dialog. Must be rendered inside `DialogHeader` or directly within `DialogContent`.

<ParamField path="asChild" type="boolean" />

## DialogTrigger

The element that opens the dialog when clicked.

On touch / pen, a press-and-release that crosses a small movement threshold is treated as a drag and the resulting synthetic click is suppressed — defends against Android Chrome opening the dialog on a scroll-drag-end.

Accepts all props of the underlying Radix `DialogPrimitive.Trigger` primitive, plus `className`.

## Exported types

Also exported from `@fanvue/ui`: `DialogBodyProps`, `DialogCloseProps`, `DialogContentProps`, `DialogDescriptionProps`, `DialogFooterProps`, `DialogHeaderProps`, `DialogOverlayProps`, `DialogProps`, `DialogTitleProps`, `DialogTriggerProps`.

***

**Setup:** [Installation](/docs/ui/installation) · [Theming](/docs/ui/theming)
