# AlertDialog (/docs/alert-dialog)





## API Reference [#api-reference]

<BaseUIReference href="https://base-ui.com/react/components/alert-dialog" />

## Basic [#basic]

<Preview cssProperties="alertDialogPlaygroundCssProperties">
  <AlertDialogExample />

  <Preview.Code>
    {`
          import {
            AlertDialog,
            AlertDialogTrigger,
            Button,
            AlertDialogContent,
            AlertDialogHeader,
            AlertDialogTitle,
            AlertDialogCloseIcon,
            AlertDialogDescription,
            AlertDialogFooter,
            AlertDialogCancel,
            AlertDialogAction,
          } from "moduix";

          export function AlertDialogDemo() {
            return (
              <AlertDialog>
                <AlertDialogTrigger render={<Button />}>Discard draft</AlertDialogTrigger>
                <AlertDialogContent>
                  <AlertDialogHeader>
                    <AlertDialogTitle>Discard draft?</AlertDialogTitle>
                    <AlertDialogCloseIcon />
                    <AlertDialogDescription>You cannot undo this action.</AlertDialogDescription>
                  </AlertDialogHeader>
                  <AlertDialogFooter>
                    <AlertDialogCancel render={<Button variant="outline" />}>Cancel</AlertDialogCancel>
                    <AlertDialogAction render={<Button />}>Discard</AlertDialogAction>
                  </AlertDialogFooter>
                </AlertDialogContent>
              </AlertDialog>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSSProperties>
    {(context) => <AlertDialogCssPropertiesPanel {...context} />}
  </Preview.CSSProperties>

  <Preview.CSSPlayground>
    {(context) => <AlertDialogCssPlaygroundPanel {...context} />}
  </Preview.CSSPlayground>
</Preview>

## Anatomy [#anatomy]

`AlertDialog` is built around a root state container and a content surface that renders the
portal, backdrop, and viewport for you. Keep title, description, and actions inside
`AlertDialogContent` so focus management and accessibility labeling stay connected.

```text
AlertDialog
├─ AlertDialogTrigger
└─ AlertDialogContent
   ├─ AlertDialogHeader
   │  ├─ AlertDialogTitle
   │  ├─ AlertDialogCloseIcon (optional)
   │  └─ AlertDialogDescription
   ├─ AlertDialogBody (optional)
   └─ AlertDialogFooter
      ├─ AlertDialogCancel
      └─ AlertDialogAction
```

```tsx
<AlertDialog>
  <AlertDialogTrigger render={<Button />}>Discard draft</AlertDialogTrigger>
  <AlertDialogContent>
    <AlertDialogHeader>
      <AlertDialogTitle>Discard draft?</AlertDialogTitle>
      <AlertDialogCloseIcon />
      <AlertDialogDescription>You cannot undo this action.</AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel render={<Button variant="outline" />}>Cancel</AlertDialogCancel>
      <AlertDialogAction render={<Button />}>Discard</AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>
```

| Part                     | Role                                                                                              |
| ------------------------ | ------------------------------------------------------------------------------------------------- |
| `AlertDialog`            | Root state machine. Manages open state, controlled/uncontrolled behavior, and close interactions. |
| `AlertDialogTrigger`     | Opens the confirmation dialog. Usually rendered as a button via `render`.                         |
| `AlertDialogContent`     | Main dialog surface. Also renders portal/backdrop/viewport and exposes escape hatches via props.  |
| `AlertDialogHeader`      | Groups heading content for the top area of the popup.                                             |
| `AlertDialogTitle`       | Required accessible title for the dialog.                                                         |
| `AlertDialogDescription` | Optional supporting text that explains the consequence of the action.                             |
| `AlertDialogCloseIcon`   | Optional dismiss button in the header; accepts custom icon children.                              |
| `AlertDialogBody`        | Optional scrollable/content area between header and footer for longer details.                    |
| `AlertDialogFooter`      | Layout container for decision actions.                                                            |
| `AlertDialogCancel`      | Secondary action that closes without confirming.                                                  |
| `AlertDialogAction`      | Primary confirmation action; closes after activation unless custom logic keeps it open.           |
| `portal` slot            | Mount layer outside normal layout flow. Useful for stacking and isolating dialog rendering roots. |
| `backdrop` slot          | Overlay behind the popup. Dim background content and communicate modal focus.                     |
| `viewport` slot          | Alignment container around the popup. Controls placement and outer spacing behavior.              |

Use default slot styling in most cases. Customize `portal` when integrating with a specific
mount container or layered app shell, `backdrop` for brand-specific overlay tone/interaction,
and `viewport` when placement rules differ (for example top-aligned or edge-biased dialogs).

## Composition [#composition]

Use `AlertDialog` for root state and behavior props such as `open`, `defaultOpen`,
`onOpenChange`, `triggerId`, `defaultTriggerId`, `actionsRef`, and `handle`. Triggers support
Base UI `handle`, `payload`, `id`, `nativeButton`, `render`, state-based `className`, and
state-based `style` props.

`className` styles the visible popup. `classNames` styles the internal service slots that are
hidden from the default composition. `AlertDialogContent` accepts the popup props from Base UI,
including `initialFocus`, `finalFocus`, `render`, state-based `className`, and state-based `style`.
Use `container`, `slotProps`, and `withBackdrop={false}` when you need service-slot escape hatches:

```tsx
<AlertDialogContent
  className={styles.popup}
  classNames={{
    portal: styles.portal,
    backdrop: styles.backdrop,
    viewport: styles.viewport,
  }}
  slotProps={{
    portal: { keepMounted: true },
    backdrop: { forceRender: true },
    viewport: { render: <div /> },
  }}
/>
```

The service slots still expose the Base UI data attributes on their rendered elements: backdrop,
viewport, and popup receive open/closed and transition attributes; viewport and popup also receive
nested-dialog attributes. The popup also provides Base UI's `--nested-dialogs` variable for nested
dialog styling.

## Examples [#examples]

### Controlled [#controlled]

Control the open state from React when the dialog needs to coordinate with other UI.

<Preview>
  <ControlledAlertDialogExample />

  <Preview.Code>
    {`
          import {
            AlertDialog,
            AlertDialogTrigger,
            Button,
            AlertDialogContent,
            AlertDialogHeader,
            AlertDialogTitle,
            AlertDialogDescription,
            AlertDialogFooter,
            AlertDialogCancel,
            AlertDialogAction,
          } from "moduix";
          import { useState } from "react";

          export function ControlledAlertDialogDemo() {
            const [open, setOpen] = useState(false);

            return (
              <AlertDialog open={open} onOpenChange={setOpen}>
                <AlertDialogTrigger render={<Button />}>Open controlled dialog</AlertDialogTrigger>
                <AlertDialogContent>
                  <AlertDialogHeader>
                    <AlertDialogTitle>Publish changes?</AlertDialogTitle>
                    <AlertDialogDescription>
                      This will make the latest version visible to all users.
                    </AlertDialogDescription>
                  </AlertDialogHeader>
                  <AlertDialogFooter>
                    <AlertDialogCancel render={<Button variant="outline" />}>
                      Back to editing
                    </AlertDialogCancel>
                    <AlertDialogAction render={<Button />}>Publish</AlertDialogAction>
                  </AlertDialogFooter>
                </AlertDialogContent>
              </AlertDialog>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Handle [#handle]

Use `createAlertDialogHandle` when a confirmation is opened from detached triggers or from
imperative event handlers. Pass `payload` to detached triggers when the dialog copy depends on the
source action.

<Preview>
  <AlertDialogHandleExample />

  <Preview.Code>
    {`
          import {
            createAlertDialogHandle,
            AlertDialogTrigger,
            Button,
            AlertDialog,
            AlertDialogContent,
            AlertDialogHeader,
            AlertDialogTitle,
            AlertDialogDescription,
            AlertDialogFooter,
            AlertDialogCancel,
            AlertDialogAction,
          } from "moduix";
          import { useMemo, Fragment } from "react";

          export function AlertDialogHandleDemo() {
            const alertDialogHandle = useMemo(() => createAlertDialogHandle(), []);

            return (
              <Fragment>
                <AlertDialogTrigger handle={alertDialogHandle} render={<Button variant="outline" />}>
                  Open from detached trigger
                </AlertDialogTrigger>
                <Button type="button" onClick={() => alertDialogHandle.open(null)}>
                  Open programmatically
                </Button>

                <AlertDialog handle={alertDialogHandle}>
                  <AlertDialogContent>
                    <AlertDialogHeader>
                      <AlertDialogTitle>Delete workspace?</AlertDialogTitle>
                      <AlertDialogDescription>
                        This alert dialog is connected via createAlertDialogHandle().
                      </AlertDialogDescription>
                    </AlertDialogHeader>
                    <AlertDialogFooter>
                      <AlertDialogCancel render={<Button variant="outline" />}>Cancel</AlertDialogCancel>
                      <AlertDialogAction render={<Button />}>Delete</AlertDialogAction>
                    </AlertDialogFooter>
                  </AlertDialogContent>
                </AlertDialog>
              </Fragment>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Scrollable Content [#scrollable-content]

Place `ScrollArea` inside `AlertDialogBody` for longer confirmation details.

<Preview>
  <ScrollableAlertDialogExample />

  <Preview.Code>
    {`
          import {
            AlertDialog,
            AlertDialogTrigger,
            Button,
            AlertDialogContent,
            AlertDialogHeader,
            AlertDialogTitle,
            AlertDialogCloseIcon,
            AlertDialogDescription,
            AlertDialogBody,
            AlertDialogFooter,
            AlertDialogCancel,
            AlertDialogAction,
            ScrollArea,
          } from "moduix";

          export function ScrollableAlertDialogDemo() {
            return (
              <AlertDialog>
                <AlertDialogTrigger render={<Button />}>Delete project</AlertDialogTrigger>
                <AlertDialogContent>
                  <AlertDialogHeader>
                    <AlertDialogTitle>Delete project?</AlertDialogTitle>
                    <AlertDialogCloseIcon />
                    <AlertDialogDescription>
                      This removes all deployment environments and API keys.
                    </AlertDialogDescription>
                  </AlertDialogHeader>
                  <AlertDialogBody className={styles.scrollBody}>
                    <ScrollArea
                      className={styles.scrollArea}
                      classNames={{ content: styles.scrollContent }}
                    >
                      {sections.map((item) => (
                        <section key={item.title}>
                          <h3>{item.title}</h3>
                          <p>{item.body}</p>
                        </section>
                      ))}
                    </ScrollArea>
                  </AlertDialogBody>
                  <AlertDialogFooter>
                    <AlertDialogCancel render={<Button variant="outline" />}>Cancel</AlertDialogCancel>
                    <AlertDialogAction render={<Button />}>Delete permanently</AlertDialogAction>
                  </AlertDialogFooter>
                </AlertDialogContent>
              </AlertDialog>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .scrollBody {
            height: min(26rem, calc(100dvh - var(--spacing-10)));
            overflow: hidden;
          }

          .scrollArea {
            height: 100%;
            min-height: 0;
          }

          .scrollContent {
            display: grid;
            gap: var(--spacing-4);
            padding-right: var(--spacing-5);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const sections = [
            {
              title: 'What this surface is for',
              body: 'Use temporary surfaces for focused tasks that should keep users in the current page context.',
            },
            {
              title: 'Keyboard and focus',
              body: 'Tab and Shift+Tab should stay predictable while Escape or explicit controls request close.',
            },
            {
              title: 'Viewport overflow',
              body: 'Keep the container visible and place long content in a dedicated scrollable inner region.',
            },
            {
              title: 'Close affordances',
              body: 'Always provide an explicit close action when the surface can be dismissed by the user.',
            },
            {
              title: 'Mobile ergonomics',
              body: 'Keep touch targets reachable and avoid cramped headers on narrow viewports.',
            },
            {
              title: 'Persistent panels',
              body: 'For persistent workflows, keep the important controls fixed and scroll only the supporting content.',
            },
            {
              title: 'Status updates',
              body: 'After completion, close the surface and show an inline confirmation or toast.',
            },
            {
              title: 'Error handling',
              body: 'When an action fails, keep the user in context and show the recovery step near the failed control.',
            },
            {
              title: 'Long descriptions',
              body: 'Dense explanatory copy should remain readable without pushing primary actions out of reach.',
            },
            {
              title: 'Scrolling feedback',
              body: 'Visible scrollbars, fades, or edge states help users understand that additional content is available.',
            },
            {
              title: 'Footer behavior',
              body: 'Footer actions should stay stable when the user reviews long terms, warnings, or settings.',
            },
            {
              title: 'Review checklist',
              body: 'Use repeated sections to test keyboard scrolling, wheel scrolling, touch scrolling, and drag gestures.',
            },
            {
              title: 'Final confirmation',
              body: 'The final section should be reachable without layout jumps or hidden content at the bottom edge.',
            },
          ];
        `}
  </Preview.Data>
</Preview>

### Custom Close Icon [#custom-close-icon]

Pass children to `AlertDialogCloseIcon` to use any icon from your application or icon library.

<Preview>
  <CustomCloseIconAlertDialogExample />

  <Preview.Code>
    {`
          import {
            AlertDialog,
            AlertDialogTrigger,
            Button,
            AlertDialogContent,
            AlertDialogHeader,
            AlertDialogTitle,
            AlertDialogCloseIcon,
            CloseLineIcon,
            AlertDialogDescription,
            AlertDialogFooter,
            AlertDialogCancel,
            AlertDialogAction,
          } from "moduix";

          export function CustomCloseIconAlertDialogDemo() {
            return (
              <AlertDialog>
                <AlertDialogTrigger render={<Button />}>Archive workspace</AlertDialogTrigger>
                <AlertDialogContent>
                  <AlertDialogHeader>
                    <AlertDialogTitle>Archive workspace?</AlertDialogTitle>
                    <AlertDialogCloseIcon
                      aria-label="Close archive dialog"
                      className={styles.customCloseIcon}
                    >
                      <CloseLineIcon />
                    </AlertDialogCloseIcon>
                    <AlertDialogDescription>
                      Team members will lose access until the workspace is restored.
                    </AlertDialogDescription>
                  </AlertDialogHeader>
                  <AlertDialogFooter>
                    <AlertDialogCancel render={<Button variant="outline" />}>Cancel</AlertDialogCancel>
                    <AlertDialogAction render={<Button />}>Archive</AlertDialogAction>
                  </AlertDialogFooter>
                </AlertDialogContent>
              </AlertDialog>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .customCloseIcon {
            --alert-dialog-close-icon-color: var(--color-muted-foreground);
          }
        `}
  </Preview.CSS>
</Preview>

### Custom Styles [#custom-styles]

Use `className` for the popup and `classNames` for the automatically rendered portal, backdrop,
and viewport.

<Preview>
  <CustomStylesAlertDialogExample />

  <Preview.Code>
    {`
          import {
            AlertDialog,
            AlertDialogTrigger,
            Button,
            AlertDialogContent,
            AlertDialogHeader,
            AlertDialogTitle,
            AlertDialogDescription,
            AlertDialogFooter,
            AlertDialogCancel,
            AlertDialogAction,
          } from "moduix";

          export function CustomStylesAlertDialogDemo() {
            return (
              <AlertDialog>
                <AlertDialogTrigger render={<Button />}>Reset environment</AlertDialogTrigger>
                <AlertDialogContent
                  className={styles.customPopup}
                  classNames={{
                    portal: styles.customPortal,
                    backdrop: styles.customBackdrop,
                    viewport: styles.customViewport,
                  }}
                >
                  <AlertDialogHeader>
                    <AlertDialogTitle>Reset environment?</AlertDialogTitle>
                    <AlertDialogDescription>
                      All runtime variables will return to their default values.
                    </AlertDialogDescription>
                  </AlertDialogHeader>
                  <AlertDialogFooter>
                    <AlertDialogCancel render={<Button variant="outline" />}>Cancel</AlertDialogCancel>
                    <AlertDialogAction render={<Button />}>Reset</AlertDialogAction>
                  </AlertDialogFooter>
                </AlertDialogContent>
              </AlertDialog>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .customPortal {
            pointer-events: auto;
          }

          .customBackdrop {
            background-color: rgb(15 23 42 / 0.56);
          }

          .customViewport {
            align-items: start;
            padding-top: var(--spacing-10);
          }

          .customPopup {
            --alert-dialog-width: 28rem;
            --alert-dialog-radius: var(--radius-md);
          }
        `}
  </Preview.CSS>
</Preview>
