---
title: Dialog
description: "A window overlaid on the page, rendering the content underneath inert."
---

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

export default function DialogDemo() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline" />}>
        Open dialog
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Delete project</DialogTitle>
          <DialogDescription>
            This action cannot be undone. The project and all of its data
            will be permanently removed.
          </DialogDescription>
        </DialogHeader>
        <DialogFooter>
          <DialogClose render={<Button variant="ghost" />}>Cancel</DialogClose>
          <DialogClose render={<Button variant="destructive" />}>
            Delete
          </DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
```

## Install

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

## Usage

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

## Composition

```tsx
<Dialog>
  <DialogTrigger />
  <DialogContent>
    <DialogHeader>
      <DialogTitle />
      <DialogDescription />
    </DialogHeader>
    <DialogFooter>
      <DialogClose />
    </DialogFooter>
  </DialogContent>
</Dialog>
```

## Custom width

Override the content width with the `style` prop.

```tsx
import * as stylex from '@stylexjs/stylex';

import { container } from '@/lib/constants.stylex';

import { Button } from '@/components/ui/button';
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';

export default function DialogCustom() {
  return (
    <Dialog>
      <DialogTrigger render={<Button />}>Rename</DialogTrigger>
      <DialogContent style={styles.narrow}>
        <DialogHeader>
          <DialogTitle>Rename project</DialogTitle>
          <DialogDescription>Give your project a new name.</DialogDescription>
        </DialogHeader>
        <Input defaultValue="madeui" />
        <DialogFooter>
          <DialogClose render={<Button variant="ghost" />}>Cancel</DialogClose>
          <DialogClose render={<Button />}>Save</DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

const styles = stylex.create({
  narrow: {
    width: container.md,
  },
});
```

## Scrollable content

Give `DialogContent` a `maxHeight` and wrap the long content in its own
scrollable container — the header and footer stay put.

```tsx
import * as stylex from '@stylexjs/stylex';

import { Button } from '@/components/ui/button';
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from '@/components/ui/dialog';
import { space } from '@/lib/constants.stylex';

export default function DialogScrollable() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline" />}>
        View terms
      </DialogTrigger>
      <DialogContent style={styles.content}>
        <DialogHeader>
          <DialogTitle>Terms of Service</DialogTitle>
          <DialogDescription>
            Last updated February 2026. Please read before continuing.
          </DialogDescription>
        </DialogHeader>
        <div {...stylex.props(styles.body)}>
          <p>
            1. Acceptance of terms. By accessing or using this service, you
            agree to be bound by these terms and all applicable laws and
            regulations.
          </p>
          <p>
            2. Use license. Permission is granted to temporarily use this
            service for personal, non-commercial purposes only. This is the
            grant of a license, not a transfer of title.
          </p>
          <p>
            3. Account responsibilities. You are responsible for maintaining
            the confidentiality of your account credentials and for all
            activity that occurs under your account.
          </p>
          <p>
            4. Service availability. We do not guarantee that the service
            will be uninterrupted, timely, secure, or error-free, and we
            reserve the right to modify or discontinue it at any time.
          </p>
          <p>
            5. Limitation of liability. In no event shall we be liable for
            any indirect, incidental, special, or consequential damages
            arising out of your use of the service.
          </p>
          <p>
            6. Changes to terms. We may revise these terms at any time. By
            continuing to use the service after changes take effect, you
            agree to the revised terms.
          </p>
          <p>
            7. Termination. We may suspend or terminate your access at any
            time, without notice, for conduct that violates these terms.
          </p>
        </div>
        <DialogFooter>
          <DialogClose render={<Button variant="ghost" />}>Decline</DialogClose>
          <DialogClose render={<Button />}>Accept</DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

const styles = stylex.create({
  content: {
    maxHeight: `calc(100dvh - ${space.s16})`,
  },
  body: {
    display: 'flex',
    flexDirection: 'column',
    flex: 1,
    gap: space.s3,
    minHeight: 0,
    overflowY: 'auto',
  },
});
```

## No close button

Set `showCloseButton={false}` and close the dialog through your own footer
buttons instead.

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

export default function DialogNoCloseButton() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline" />}>
        Open dialog
      </DialogTrigger>
      <DialogContent showCloseButton={false}>
        <DialogHeader>
          <DialogTitle>Confirm your email</DialogTitle>
          <DialogDescription>
            We sent a confirmation link to your inbox. This dialog only
            closes through the buttons below.
          </DialogDescription>
        </DialogHeader>
        <DialogFooter>
          <DialogClose render={<Button variant="ghost" />}>
            Resend later
          </DialogClose>
          <DialogClose render={<Button />}>Got it</DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
```

## API reference

Built on [Base UI Dialog](https://base-ui.com/react/components/dialog). The tables below cover the props this library adds or changes — every other prop is forwarded to the underlying Base UI part; see the [Base UI Dialog API reference](https://base-ui.com/react/components/dialog#api-reference) for the full list.

### Dialog (Root)

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `open` | `boolean` | — | Controlled open state. |
| `defaultOpen` | `boolean` | `false` |  |
| `onOpenChange` | `(open: boolean) => void` | — |  |

### DialogContent

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `showCloseButton` | `boolean` | `true` | Renders an X close button in the top-right corner. |
| `style` | `StyleXStyles` | — | StyleX styles merged last — always win over the component's own styles. |

### Styling

`DialogContent`, `DialogOverlay`, `DialogHeader`, `DialogFooter`, `DialogTitle`, `DialogDescription` accept `style` (`StyleXStyles`, merged last so caller overrides always win) plus all native props of the element they render.

`DialogTrigger` / `DialogClose` accept `render`: `<DialogTrigger render={<Button />}>`. Always include a `DialogTitle` for accessibility.

Theming note: dialogs portal to `<body>` — apply themes (e.g. `darkTheme`) to `<html>`, not a wrapper element.
