---
id: "packages/active-ui/alert-dialog"
title: "AlertDialog"
description: "A dialog that interrupts with a question that must be answered, such as confirming a deletion, without a way to click it away."
url: "/packages/active-ui/alert-dialog"
locale: "en"
revision: "69c6d3b2435f2af9"
---

# AlertDialog

An alert dialog asks a question the reader must answer before going on: delete this, discard those
changes. Unlike a [dialog](/packages/active-ui/dialog), it does not close on a click outside, and it
is announced as an alert. For the common case of confirming one action with a busy state, use the
composed [ConfirmActionDialog](/packages/active-ui/confirm-action-dialog), which is built on it.

**Example: A destructive question and a small one**

Client: Runs entirely in your browser. Source: `examples/ui/alert-dialog.tsx`.

```tsx
// apps/docs/examples/ui/alert-dialog.tsx
'use client';

import {useState} from 'react';
import {Trash2Icon} from 'lucide-react';

import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogMedia,
  AlertDialogTitle,
  AlertDialogTrigger,
} from '@workspace/active-ui/components/primitives/alert-dialog';
import {Button} from '@workspace/active-ui/components/primitives/button';

/**
 * A question that must be answered before the reader goes on: delete a
 * scheme, or discard unsaved changes. The cancel button closes the dialog by
 * itself; the action is an ordinary button, so it does its work and closes
 * the dialog through the open state.
 */
export default function AlertDialogExample() {
  const [open, setOpen] = useState(false);
  const [deleted, setDeleted] = useState(false);
  return (
    <div className="flex flex-col items-center gap-4">
      <div className="flex flex-wrap justify-center gap-2">
        <AlertDialog open={open} onOpenChange={setOpen}>
          <AlertDialogTrigger
            render={<Button variant="destructive" disabled={deleted} />}
          >
            Delete scheme
          </AlertDialogTrigger>
          <AlertDialogContent>
            <AlertDialogHeader>
              <AlertDialogMedia>
                <Trash2Icon aria-hidden />
              </AlertDialogMedia>
              <AlertDialogTitle>Delete Pension scheme 2026?</AlertDialogTitle>
              <AlertDialogDescription>
                The scheme and its 214 participants are removed. This cannot be
                undone.
              </AlertDialogDescription>
            </AlertDialogHeader>
            <AlertDialogFooter>
              <AlertDialogCancel>Keep the scheme</AlertDialogCancel>
              <AlertDialogAction
                variant="destructive"
                onClick={() => {
                  setDeleted(true);
                  setOpen(false);
                }}
              >
                Delete
              </AlertDialogAction>
            </AlertDialogFooter>
          </AlertDialogContent>
        </AlertDialog>

        <AlertDialog>
          <AlertDialogTrigger render={<Button variant="outline" />}>
            Leave the form
          </AlertDialogTrigger>
          <AlertDialogContent size="sm">
            <AlertDialogHeader>
              <AlertDialogTitle>Discard your changes?</AlertDialogTitle>
              <AlertDialogDescription>
                The changes you made to this form are not saved.
              </AlertDialogDescription>
            </AlertDialogHeader>
            <AlertDialogFooter>
              <AlertDialogCancel>Stay</AlertDialogCancel>
              <AlertDialogCancel variant="default">Discard</AlertDialogCancel>
            </AlertDialogFooter>
          </AlertDialogContent>
        </AlertDialog>
      </div>
      <p className="text-muted-foreground text-sm" aria-live="polite">
        {deleted ? 'The scheme was deleted.' : 'The scheme exists.'}
      </p>
      {deleted ? (
        <Button variant="ghost" size="sm" onClick={() => setDeleted(false)}>
          Reset the example
        </Button>
      ) : null}
    </div>
  );
}
```

## Usage

```tsx
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from '@workspace/active-ui/components/primitives/alert-dialog';

<AlertDialog open={open} onOpenChange={setOpen}>
  <AlertDialogTrigger render={<Button variant="destructive" />}>
    Delete
  </AlertDialogTrigger>
  <AlertDialogContent>
    <AlertDialogHeader>
      <AlertDialogTitle>Delete this scheme?</AlertDialogTitle>
      <AlertDialogDescription>This cannot be undone.</AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>Cancel</AlertDialogCancel>
      <AlertDialogAction variant="destructive" onClick={remove}>
        Delete
      </AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>;
```

## Anatomy

- `AlertDialog`: the open state; `open` and `onOpenChange`, or uncontrolled.
- `AlertDialogTrigger`: the element that opens it; `render` uses your own button.
- `AlertDialogContent`: the window with its backdrop. `size`: `default`, or `sm` for a short
  question with the buttons side by side.
- `AlertDialogHeader` with `AlertDialogTitle` and `AlertDialogDescription`; `AlertDialogMedia` for
  an icon beside the title.
- `AlertDialogFooter` with the answers:

  - `AlertDialogCancel` **closes** the dialog (Base UI's close part, styled as an `outline`
    button, with `variant` and `size`);
  - `AlertDialogAction` is a plain [Button](/packages/active-ui/button): it **does not close** the
    dialog. Do the work in its `onClick` and close through `onOpenChange`, or after the work
    finished, as in the example. That keeps a failed action from closing the question.

## What your application provides

- The title, the description and the labels of both answers, worded as the outcome ("Delete",
  "Keep the scheme"), not "OK".
- The open state when the action is more than a click, and the work the action starts.

## Accessibility and keyboard

- The window is a `role="alertdialog"`, named by its title and described by its description.
- Opening moves the focus inside; `Tab` stays inside. `Escape` closes it like the cancel button.
- A click on the backdrop does nothing: the question has to be answered.
- On close, the focus returns to the trigger.

## API

### AlertDialogContent

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `size` | `"default" \| "sm"` | no | The width and layout: `default`, or `sm` for a short question with the answers side by side. |
| `finalFocus` | `boolean \| RefObject<HTMLElement \| null> \| ((closeType: InteractionType) => boolean \| HTMLElement \| null \| void)` | no | 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. |
| `initialFocus` | `boolean \| RefObject<HTMLElement \| null> \| ((openType: InteractionType) => boolean \| HTMLElement \| null \| void)` | no | 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. |
| `render` | `ComponentRenderFn<HTMLProps, DialogPopupState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | 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. |

### AlertDialogCancel

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `size` | `"default" \| "icon" \| "icon-lg" \| "icon-sm" \| "icon-xs" \| "lg" \| "sm" \| "xs" \| null` | no | The height and padding. `xs`, `sm`, `default` and `lg` for a button with text; `icon-xs`, `icon-sm`, `icon` and `icon-lg` for a square button with only an icon, which then needs an accessible name (`aria-label`). |
| `variant` | `"default" \| "destructive" \| "ghost" \| "link" \| "outline" \| "secondary" \| null` | no | The visual weight of the button. `default` for the main action of a screen or form; `outline` and `secondary` for other actions; `ghost` for a quiet action in a toolbar or a list; `destructive` for an action that deletes or cannot be undone; `link` for an action that reads as a link. Every variant but `link` sinks 1px while pressed; a link-styled action keeps its text and icon in place (`button-link-still`). |
| `nativeButton` | `boolean` | no | 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>`). |
| `render` | `ComponentRenderFn<HTMLProps, DialogCloseState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | 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. |

### Other parts

**AlertDialog**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `actionsRef` | `RefObject<DialogRootActions \| null>` | no | A ref to imperative actions. - `unmount`: Ends the closing phase of the alert dialog after an externally controlled closing animation finishes. Call `preventUnmountOnClose()` in `onOpenChange` first, otherwise the alert dialog completes closing on its own. Whether it leaves the DOM is decided by `keepMounted` on the portal. - `close`: Closes the alert dialog imperatively when called. |
| `children` | `PayloadChildRenderFunction<unknown> \| ReactNode` | no | The content of the dialog. This can be a regular React node or a render function that receives the `payload` of the active trigger. |
| `defaultOpen` | `boolean` | no | Whether the dialog is initially open.  To render a controlled dialog, use the `open` prop instead. |
| `defaultTriggerId` | `string \| null` | no | 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. |
| `handle` | `AlertDialogHandle<unknown>` | no | A handle to associate the alert dialog with a trigger. If specified, allows external triggers to control the alert dialog's open state. Can be created with the AlertDialog.createHandle() method. |
| `onOpenChange` | `((open: boolean, eventDetails: AlertDialogRoot.ChangeEventDetails) => void)` | no | Event handler called when the alert dialog is opened or closed. |
| `onOpenChangeComplete` | `((open: boolean) => void)` | no | Event handler called after any animations complete when the dialog is opened or closed. |
| `open` | `boolean` | no | Whether the dialog is currently open. |
| `triggerId` | `string \| null` | no | 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). |

**AlertDialogAction**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `size` | `"default" \| "icon" \| "icon-lg" \| "icon-sm" \| "icon-xs" \| "lg" \| "sm" \| "xs" \| null` | no | The height and padding. `xs`, `sm`, `default` and `lg` for a button with text; `icon-xs`, `icon-sm`, `icon` and `icon-lg` for a square button with only an icon, which then needs an accessible name (`aria-label`). |
| `variant` | `"default" \| "destructive" \| "ghost" \| "link" \| "outline" \| "secondary" \| null` | no | The visual weight of the button. `default` for the main action of a screen or form; `outline` and `secondary` for other actions; `ghost` for a quiet action in a toolbar or a list; `destructive` for an action that deletes or cannot be undone; `link` for an action that reads as a link. Every variant but `link` sinks 1px while pressed; a link-styled action keeps its text and icon in place (`button-link-still`). |
| `focusableWhenDisabled` | `boolean` | no | Whether the button should be focusable when disabled. |
| `nativeButton` | `boolean` | no | 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>`). |
| `render` | `ComponentRenderFn<HTMLProps, ButtonState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | 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. |

**AlertDialogDescription**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `render` | `ComponentRenderFn<HTMLProps, DialogDescriptionState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | 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. |

**AlertDialogFooter**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |

AlertDialogFooter has no props of its own.

**AlertDialogHeader**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |

AlertDialogHeader has no props of its own.

**AlertDialogMedia**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |

AlertDialogMedia has no props of its own.

**AlertDialogOverlay**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `forceRender` | `boolean` | no | Whether the backdrop is forced to render even when nested. |
| `render` | `ComponentRenderFn<HTMLProps, DialogBackdropState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | 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. |

**AlertDialogPortal**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `container` | `HTMLElement \| RefObject<HTMLElement \| ShadowRoot \| null> \| ShadowRoot \| null` | no | A parent element to render the portal element into. |
| `keepMounted` | `boolean` | no | Whether to keep the portal mounted in the DOM while the popup is hidden. |
| `render` | `ComponentRenderFn<HTMLProps, DialogPortalState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | 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. |

**AlertDialogTitle**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `render` | `ComponentRenderFn<HTMLProps, DialogTitleState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | 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. |

**AlertDialogTrigger**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `handle` | `AlertDialogHandle<unknown>` | no | A handle to associate the trigger with an alert dialog. Can be created with the AlertDialog.createHandle() method. |
| `id` | `string` | no | 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). |
| `nativeButton` | `boolean` | no | 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>`). |
| `payload` | `unknown` | no | A payload to pass to the dialog when it is opened. |
| `render` | `ComponentRenderFn<HTMLProps, DialogTriggerState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | 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. |

> [!NOTE]
> **Based on shadcn/ui**
>
> This page adapts the Alert Dialog documentation of shadcn/ui (MIT licence) to
> this package; see [Third-party notices](/third-party-notices).
