27.4k

Alert DialogNew

Modal dialog for critical confirmations requiring user attention and explicit action

Import

import { AlertDialog } from '@heroui/react';

Usage

"use client";

import {AlertDialog, Button} from "@heroui/react";

export function Default() {
  return (
    <AlertDialog>
      <Button variant="danger">Delete Project</Button>
      <AlertDialog.Container>
        <AlertDialog.Dialog className="sm:max-w-[400px]">
          {({close}) => (
            <>
              <AlertDialog.Header>
                <AlertDialog.Icon status="danger" />
                <AlertDialog.Heading>Delete project permanently?</AlertDialog.Heading>
              </AlertDialog.Header>
              <AlertDialog.Body>
                <p>
                  This will permanently delete <strong>My Awesome Project</strong> and all of its
                  data. This action cannot be undone.
                </p>
              </AlertDialog.Body>
              <AlertDialog.Footer>
                <Button variant="tertiary" onPress={close}>
                  Cancel
                </Button>
                <Button variant="danger" onPress={close}>
                  Delete Project
                </Button>
              </AlertDialog.Footer>
            </>
          )}
        </AlertDialog.Dialog>
      </AlertDialog.Container>
    </AlertDialog>
  );
}

Anatomy

Import the AlertDialog component and access all parts using dot notation.

import { AlertDialog, Button } from '@heroui/react';

export default () => (
  <AlertDialog>
    <Button>Open Alert Dialog</Button>
    <AlertDialog.Container>
      <AlertDialog.Dialog>
        <AlertDialog.CloseTrigger />  {/* Optional: Close button */}
        <AlertDialog.Header>
          <AlertDialog.Icon />  {/* Optional: Status icon */}
          <AlertDialog.Heading />
        </AlertDialog.Header>
        <AlertDialog.Body />
        <AlertDialog.Footer />
      </AlertDialog.Dialog>
    </AlertDialog.Container>
  </AlertDialog>
)

Statuses

"use client";

import {AlertDialog, Button} from "@heroui/react";

export function Statuses() {
  const examples = [
    {
      actions: {
        cancel: "Stay Signed In",
        confirm: "Sign Out",
      },
      body: "You'll need to sign in again to access your account. Any unsaved changes will be lost.",
      classNames: "bg-accent-soft text-accent-soft-foreground",
      header: "Sign out of your account?",
      status: "accent",
      trigger: "Sign Out",
    },
    {
      actions: {
        cancel: "Not Yet",
        confirm: "Mark Complete",
      },
      body: "This will mark the task as complete and notify all team members. The task will be moved to your completed list.",
      classNames: "bg-success-soft text-success-soft-foreground",
      header: "Complete this task?",
      status: "success",
      trigger: "Complete Task",
    },
    {
      actions: {
        cancel: "Keep Editing",
        confirm: "Discard",
      },
      body: "You have unsaved changes that will be permanently lost. Are you sure you want to discard them?",
      classNames: "bg-warning-soft text-warning-soft-foreground",
      header: "Discard unsaved changes?",
      status: "warning",
      trigger: "Discard Changes",
    },
    {
      actions: {
        cancel: "Cancel",
        confirm: "Delete Account",
      },
      body: "This will permanently delete your account and remove all your data from our servers. This action is irreversible.",
      classNames: "bg-danger-soft text-danger-soft-foreground",
      header: "Delete your account?",
      status: "danger",
      trigger: "Delete Account",
    },
  ] as const;

  return (
    <div className="flex flex-wrap gap-4">
      {examples.map(({actions, body, classNames, header, status, trigger}) => (
        <AlertDialog key={status}>
          <Button className={classNames}>{trigger}</Button>
          <AlertDialog.Container>
            <AlertDialog.Dialog className="sm:max-w-[400px]">
              {({close}) => (
                <>
                  <AlertDialog.Header>
                    <AlertDialog.Icon status={status} />
                    <AlertDialog.Heading>{header}</AlertDialog.Heading>
                  </AlertDialog.Header>
                  <AlertDialog.Body>
                    <p>{body}</p>
                  </AlertDialog.Body>
                  <AlertDialog.Footer>
                    <Button variant="tertiary" onPress={close}>
                      {actions.cancel}
                    </Button>
                    <Button variant={status === "danger" ? "danger" : "primary"} onPress={close}>
                      {actions.confirm}
                    </Button>
                  </AlertDialog.Footer>
                </>
              )}
            </AlertDialog.Dialog>
          </AlertDialog.Container>
        </AlertDialog>
      ))}
    </div>
  );
}

Placements

"use client";

import {AlertDialog, Button} from "@heroui/react";

export function Placements() {
  const placements = ["auto", "top", "center", "bottom"] as const;

  return (
    <div className="flex flex-wrap gap-4">
      {placements.map((placement) => (
        <AlertDialog key={placement}>
          <Button variant="secondary">
            {placement.charAt(0).toUpperCase() + placement.slice(1)}
          </Button>
          <AlertDialog.Container placement={placement}>
            <AlertDialog.Dialog className="sm:max-w-[400px]">
              {({close}) => (
                <>
                  <AlertDialog.Header>
                    <AlertDialog.Icon status="accent" />
                    <AlertDialog.Heading>
                      {placement === "auto"
                        ? "Auto Placement"
                        : `${placement.charAt(0).toUpperCase() + placement.slice(1)} Position`}
                    </AlertDialog.Heading>
                  </AlertDialog.Header>
                  <AlertDialog.Body>
                    <p>
                      {placement === "auto"
                        ? "Automatically positions at the bottom on mobile and center on desktop for optimal user experience."
                        : `This dialog is positioned at the ${placement} of the viewport. Critical confirmations are typically centered for maximum attention.`}
                    </p>
                  </AlertDialog.Body>
                  <AlertDialog.Footer>
                    <Button variant="tertiary" onPress={close}>
                      Cancel
                    </Button>
                    <Button onPress={close}>Confirm</Button>
                  </AlertDialog.Footer>
                </>
              )}
            </AlertDialog.Dialog>
          </AlertDialog.Container>
        </AlertDialog>
      ))}
    </div>
  );
}

Backdrop Variants

"use client";

import {AlertDialog, Button} from "@heroui/react";

export function BackdropVariants() {
  const variants = [
    {name: "solid", value: "solid"},
    {name: "blur", value: "blur"},
    {name: "transparent", value: "transparent"},
  ] as const;

  return (
    <div className="flex flex-wrap gap-4">
      {variants.map(({name, value}) => (
        <AlertDialog key={value}>
          <Button variant="secondary">{name.charAt(0).toUpperCase() + name.slice(1)}</Button>
          <AlertDialog.Container backdropVariant={value}>
            <AlertDialog.Dialog className="sm:max-w-[400px]">
              {({close}) => (
                <>
                  <AlertDialog.Header>
                    <AlertDialog.Icon status="accent" />
                    <AlertDialog.Heading>
                      {name === "solid"
                        ? "Solid Backdrop"
                        : name === "blur"
                          ? "Blur Backdrop"
                          : "Transparent Backdrop"}
                    </AlertDialog.Heading>
                  </AlertDialog.Header>
                  <AlertDialog.Body>
                    <p>
                      {name === "solid"
                        ? "A solid dark backdrop that completely obscures the background, providing maximum focus on the dialog."
                        : name === "blur"
                          ? "A blurred backdrop that softly obscures the background while maintaining visual context."
                          : "A transparent backdrop that keeps the background fully visible, useful for less critical confirmations."}
                    </p>
                  </AlertDialog.Body>
                  <AlertDialog.Footer>
                    <Button variant="tertiary" onPress={close}>
                      Cancel
                    </Button>
                    <Button onPress={close}>Confirm</Button>
                  </AlertDialog.Footer>
                </>
              )}
            </AlertDialog.Dialog>
          </AlertDialog.Container>
        </AlertDialog>
      ))}
    </div>
  );
}

Controlled State

With React.useState()

Control the dialog using React's useState hook for simple state management. Perfect for basic use cases.

Status: closed

With useOverlayState()

Use the useOverlayState hook for a cleaner API with convenient methods like open(), close(), and toggle().

Status: closed

"use client";

import {AlertDialog, Button, useOverlayState} from "@heroui/react";
import {useState} from "react";

export function Controlled() {
  const [isOpen, setIsOpen] = useState(false);

  const state = useOverlayState();

  return (
    <div className="flex max-w-md flex-col gap-8">
      <div className="flex flex-col gap-3">
        <h3 className="text-foreground text-lg font-semibold">With React.useState()</h3>
        <p className="text-muted text-pretty text-sm leading-relaxed">
          Control the dialog using React's <code className="text-foreground">useState</code> hook
          for simple state management. Perfect for basic use cases.
        </p>
        <div className="border-border bg-surface flex flex-col items-start gap-3 rounded-2xl border p-4 shadow-sm">
          <div className="flex w-full items-center justify-between">
            <p className="text-muted text-xs">
              Status:{" "}
              <span className="text-foreground font-mono font-medium">
                {isOpen ? "open" : "closed"}
              </span>
            </p>
          </div>
          <div className="flex gap-2">
            <Button size="sm" variant="secondary" onPress={() => setIsOpen(true)}>
              Open Dialog
            </Button>
            <Button size="sm" variant="tertiary" onPress={() => setIsOpen(!isOpen)}>
              Toggle
            </Button>
          </div>
        </div>

        <AlertDialog.Container isOpen={isOpen} onOpenChange={setIsOpen}>
          <AlertDialog.Dialog className="sm:max-w-[400px]">
            <AlertDialog.Header>
              <AlertDialog.Icon status="accent" />
              <AlertDialog.Heading>Controlled with useState()</AlertDialog.Heading>
            </AlertDialog.Header>
            <AlertDialog.Body>
              <p>
                This dialog is controlled by React's <code>useState</code> hook. Pass{" "}
                <code>isOpen</code> and <code>onOpenChange</code> props to manage the dialog state
                externally.
              </p>
            </AlertDialog.Body>
            <AlertDialog.Footer>
              <Button variant="tertiary" onPress={() => setIsOpen(false)}>
                Cancel
              </Button>
              <Button onPress={() => setIsOpen(false)}>Confirm</Button>
            </AlertDialog.Footer>
          </AlertDialog.Dialog>
        </AlertDialog.Container>
      </div>

      <div className="flex flex-col gap-3">
        <h3 className="text-foreground text-lg font-semibold">With useOverlayState()</h3>
        <p className="text-muted text-pretty text-sm leading-relaxed">
          Use the <code className="text-foreground">useOverlayState</code> hook for a cleaner API
          with convenient methods like <code>open()</code>, <code>close()</code>, and{" "}
          <code>toggle()</code>.
        </p>
        <div className="border-border bg-surface flex flex-col items-start gap-3 rounded-2xl border p-4 shadow-sm">
          <div className="flex w-full items-center justify-between">
            <p className="text-muted text-xs">
              Status:{" "}
              <span className="text-foreground font-mono font-medium">
                {state.isOpen ? "open" : "closed"}
              </span>
            </p>
          </div>
          <div className="flex gap-2">
            <Button size="sm" variant="secondary" onPress={state.open}>
              Open Dialog
            </Button>
            <Button size="sm" variant="tertiary" onPress={state.toggle}>
              Toggle
            </Button>
          </div>
        </div>

        <AlertDialog.Container isOpen={state.isOpen} onOpenChange={state.setOpen}>
          <AlertDialog.Dialog className="sm:max-w-[400px]">
            <AlertDialog.Header>
              <AlertDialog.Icon status="success" />
              <AlertDialog.Heading>Controlled with useOverlayState()</AlertDialog.Heading>
            </AlertDialog.Header>
            <AlertDialog.Body>
              <p>
                The <code>useOverlayState</code> hook provides dedicated methods for common
                operations. No need to manually create callbacks—just use <code>state.open()</code>,{" "}
                <code>state.close()</code>, or <code>state.toggle()</code>.
              </p>
            </AlertDialog.Body>
            <AlertDialog.Footer>
              <Button variant="tertiary" onPress={state.close}>
                Cancel
              </Button>
              <Button onPress={state.close}>Confirm</Button>
            </AlertDialog.Footer>
          </AlertDialog.Dialog>
        </AlertDialog.Container>
      </div>
    </div>
  );
}

Dismiss Behavior

Dismiss Behavior

Alert dialogs require explicit user action by design—users must click an action button to close the dialog. By default, backdrop clicks and ESC key are both disabled to prevent accidental dismissal of critical confirmations.

Default (Requires Action)

With default settings, users cannot close the dialog by clicking outside or pressing ESC. They must choose an action button.

Allow Backdrop Clicks

Set isDismissable=true to let users click outside the dialog to close it. Useful for less critical confirmations.

Full Flexibility

Enable both isDismissable=true and isKeyboardDismissDisabled=false for maximum flexibility. Users can close via backdrop, Esc, or button.

"use client";

import {AlertDialog, Button, Kbd} from "@heroui/react";
import {Icon} from "@iconify/react";

export function DismissBehavior() {
  return (
    <div className="flex max-w-md flex-col gap-8">
      <div className="flex flex-col gap-3">
        <h3 className="text-foreground text-lg font-semibold">Dismiss Behavior</h3>
        <p className="text-muted text-pretty text-sm leading-relaxed">
          Alert dialogs require explicit user action by design—users must click an action button to
          close the dialog. By default, backdrop clicks and ESC key are both disabled to prevent
          accidental dismissal of critical confirmations.
        </p>
      </div>

      <div className="flex flex-col gap-3">
        <h3 className="text-foreground text-lg font-semibold">Default (Requires Action)</h3>
        <p className="text-muted text-pretty text-sm leading-relaxed">
          With default settings, users cannot close the dialog by clicking outside or pressing ESC.
          They must choose an action button.
        </p>
        <AlertDialog>
          <Button variant="danger">Delete Project</Button>
          <AlertDialog.Container>
            <AlertDialog.Dialog className="sm:max-w-[400px]">
              {({close}) => (
                <>
                  <AlertDialog.Header>
                    <AlertDialog.Icon status="danger">
                      <Icon className="size-5" icon="gravity-ui:triangle-exclamation" />
                    </AlertDialog.Icon>
                    <AlertDialog.Heading>Delete this project?</AlertDialog.Heading>
                  </AlertDialog.Header>
                  <AlertDialog.Body>
                    <p>
                      This will permanently delete <strong>Marketing Campaign 2024</strong> and all
                      its files. This action cannot be undone.
                    </p>
                  </AlertDialog.Body>
                  <AlertDialog.Footer>
                    <Button variant="tertiary" onPress={close}>
                      Cancel
                    </Button>
                    <Button variant="danger" onPress={close}>
                      Delete Project
                    </Button>
                  </AlertDialog.Footer>
                </>
              )}
            </AlertDialog.Dialog>
          </AlertDialog.Container>
        </AlertDialog>
      </div>

      <div className="flex flex-col gap-3">
        <h3 className="text-foreground text-lg font-semibold">Allow Backdrop Clicks</h3>
        <p className="text-muted text-pretty text-sm leading-relaxed">
          Set <code className="text-foreground">isDismissable=true</code> to let users click outside
          the dialog to close it. Useful for less critical confirmations.
        </p>
        <AlertDialog>
          <Button variant="secondary">Update Available</Button>
          <AlertDialog.Container isDismissable>
            <AlertDialog.Dialog className="sm:max-w-[400px]">
              {({close}) => (
                <>
                  <AlertDialog.Header>
                    <AlertDialog.Icon status="success">
                      <Icon className="size-5" icon="gravity-ui:arrow-up-from-line" />
                    </AlertDialog.Icon>
                    <AlertDialog.Heading>Update available</AlertDialog.Heading>
                  </AlertDialog.Header>
                  <AlertDialog.Body>
                    <p>
                      Version 2.4.0 is now available. This update includes performance improvements
                      and bug fixes.
                    </p>
                  </AlertDialog.Body>
                  <AlertDialog.Footer>
                    <Button variant="tertiary" onPress={close}>
                      Later
                    </Button>
                    <Button onPress={close}>Update Now</Button>
                  </AlertDialog.Footer>
                </>
              )}
            </AlertDialog.Dialog>
          </AlertDialog.Container>
        </AlertDialog>
      </div>

      <div className="flex flex-col gap-3">
        <h3 className="text-foreground text-lg font-semibold">Full Flexibility</h3>
        <p className="text-muted text-pretty text-sm leading-relaxed">
          Enable both <code className="text-foreground">isDismissable=true</code> and{" "}
          <code className="text-foreground">isKeyboardDismissDisabled=false</code> for maximum
          flexibility. Users can close via backdrop,{" "}
          <Kbd>
            <Kbd.Content>Esc</Kbd.Content>
          </Kbd>
          , or button.
        </p>
        <AlertDialog>
          <Button variant="secondary">Show Tips</Button>
          <AlertDialog.Container isDismissable isKeyboardDismissDisabled={false}>
            <AlertDialog.Dialog className="sm:max-w-[400px]">
              {({close}) => (
                <>
                  <AlertDialog.Header>
                    <AlertDialog.Icon status="accent">
                      <Icon className="size-5" icon="gravity-ui:circle-info" />
                    </AlertDialog.Icon>
                    <AlertDialog.Heading>Pro tip</AlertDialog.Heading>
                  </AlertDialog.Header>
                  <AlertDialog.Body>
                    <p>
                      Use keyboard shortcuts to work faster. Press{" "}
                      <Kbd>
                        <Kbd.Content>Esc</Kbd.Content>
                      </Kbd>{" "}
                      to close this alert dialog.
                    </p>
                  </AlertDialog.Body>
                  <AlertDialog.Footer>
                    <Button className="w-full" onPress={close}>
                      Got it
                    </Button>
                  </AlertDialog.Footer>
                </>
              )}
            </AlertDialog.Dialog>
          </AlertDialog.Container>
        </AlertDialog>
      </div>
    </div>
  );
}

Custom Icon

"use client";

import {AlertDialog, Button} from "@heroui/react";
import {Icon} from "@iconify/react";

export function CustomIcon() {
  return (
    <AlertDialog>
      <Button variant="secondary">Reset Password</Button>
      <AlertDialog.Container>
        <AlertDialog.Dialog className="sm:max-w-[400px]">
          {({close}) => (
            <>
              <AlertDialog.Header>
                <AlertDialog.Icon status="warning">
                  <Icon className="size-5" icon="gravity-ui:lock-open" />
                </AlertDialog.Icon>
                <AlertDialog.Heading>Reset your password?</AlertDialog.Heading>
              </AlertDialog.Header>
              <AlertDialog.Body>
                <p>
                  We'll send a password reset link to your email address. You'll need to create a
                  new password to regain access to your account.
                </p>
              </AlertDialog.Body>
              <AlertDialog.Footer>
                <Button variant="tertiary" onPress={close}>
                  Cancel
                </Button>
                <Button onPress={close}>Send Reset Link</Button>
              </AlertDialog.Footer>
            </>
          )}
        </AlertDialog.Dialog>
      </AlertDialog.Container>
    </AlertDialog>
  );
}

Custom Backdrop

"use client";

import {AlertDialog, Button} from "@heroui/react";
import {Icon} from "@iconify/react";

export function CustomBackdrop() {
  return (
    <AlertDialog>
      <Button variant="danger">Delete Account</Button>
      <AlertDialog.Container
        backdropClassName="bg-gradient-to-t from-red-950/90 via-red-950/50 to-transparent dark:from-red-950/95 dark:via-red-950/60"
        backdropVariant="blur"
      >
        <AlertDialog.Dialog className="sm:max-w-[420px]">
          {({close}) => (
            <>
              <AlertDialog.Header>
                <AlertDialog.Icon status="danger">
                  <Icon className="size-5" icon="gravity-ui:triangle-exclamation" />
                </AlertDialog.Icon>
                <AlertDialog.Heading>Permanently delete your account?</AlertDialog.Heading>
              </AlertDialog.Header>
              <AlertDialog.Body>
                <p>
                  This action cannot be undone. All your data, settings, and content will be
                  permanently removed from our servers. The dramatic red backdrop emphasizes the
                  severity and irreversibility of this decision.
                </p>
              </AlertDialog.Body>
              <AlertDialog.Footer>
                <Button variant="tertiary" onPress={close}>
                  Keep Account
                </Button>
                <Button variant="danger" onPress={close}>
                  Delete Forever
                </Button>
              </AlertDialog.Footer>
            </>
          )}
        </AlertDialog.Dialog>
      </AlertDialog.Container>
    </AlertDialog>
  );
}

Custom Trigger

"use client";

import {AlertDialog, Button} from "@heroui/react";
import {Icon} from "@iconify/react";

export function CustomTrigger() {
  return (
    <AlertDialog>
      <AlertDialog.Trigger>
        <div className="border-border bg-surface hover:bg-surface-secondary group flex cursor-pointer items-center gap-3 rounded-2xl border p-4 shadow-sm transition-all hover:shadow">
          <div className="bg-danger-soft text-danger-soft-foreground flex size-12 shrink-0 items-center justify-center rounded-xl transition-transform group-hover:scale-105">
            <Icon className="size-6" icon="gravity-ui:trash-bin" />
          </div>
          <div className="flex flex-1 flex-col gap-0.5">
            <p className="text-foreground text-sm font-semibold leading-5">Delete Item</p>
            <p className="text-muted text-xs leading-relaxed">Permanently remove this item</p>
          </div>
        </div>
      </AlertDialog.Trigger>
      <AlertDialog.Container>
        <AlertDialog.Dialog className="sm:max-w-[400px]">
          {({close}) => (
            <>
              <AlertDialog.Header>
                <AlertDialog.Icon status="danger">
                  <Icon className="size-5" icon="gravity-ui:trash-bin" />
                </AlertDialog.Icon>
                <AlertDialog.Heading>Delete this item?</AlertDialog.Heading>
              </AlertDialog.Header>
              <AlertDialog.Body>
                <p>
                  This item will be permanently deleted and cannot be recovered. Are you sure you
                  want to proceed?
                </p>
              </AlertDialog.Body>
              <AlertDialog.Footer>
                <Button variant="tertiary" onPress={close}>
                  Cancel
                </Button>
                <Button variant="danger" onPress={close}>
                  Delete Item
                </Button>
              </AlertDialog.Footer>
            </>
          )}
        </AlertDialog.Dialog>
      </AlertDialog.Container>
    </AlertDialog>
  );
}

With Close Button

"use client";

import {AlertDialog, Button} from "@heroui/react";
import {Icon} from "@iconify/react";

export function WithCloseButton() {
  return (
    <AlertDialog>
      <Button variant="secondary">Show Information</Button>
      <AlertDialog.Container isDismissable>
        <AlertDialog.Dialog className="sm:max-w-[400px]">
          {({close}) => (
            <>
              <AlertDialog.CloseTrigger />
              <AlertDialog.Header>
                <AlertDialog.Icon status="default">
                  <Icon className="size-5" icon="gravity-ui:circle-info" />
                </AlertDialog.Icon>
                <AlertDialog.Heading>Less critical information</AlertDialog.Heading>
                <p className="text-muted text-sm leading-relaxed">
                  Close button and backdrop dismiss are enabled
                </p>
              </AlertDialog.Header>
              <AlertDialog.Body>
                <p>
                  For less critical confirmations, you can enable both the close button and backdrop
                  dismissal. This provides users with multiple ways to exit the dialog.
                </p>
              </AlertDialog.Body>
              <AlertDialog.Footer>
                <Button variant="tertiary" onPress={close}>
                  Cancel
                </Button>
                <Button onPress={close}>Confirm</Button>
              </AlertDialog.Footer>
            </>
          )}
        </AlertDialog.Dialog>
      </AlertDialog.Container>
    </AlertDialog>
  );
}

Custom Animations

"use client";

import {AlertDialog, Button} from "@heroui/react";
import {Icon} from "@iconify/react";

export function CustomAnimations() {
  const animations = [
    {
      classNames: [
        "data-[entering]:animate-in",
        "data-[entering]:zoom-in-95",
        "data-[entering]:fade-in-0",
        "data-[entering]:ease-[cubic-bezier(0.16,1,0.3,1)]",
        "data-[exiting]:animate-out",
        "data-[exiting]:zoom-out-95",
        "data-[exiting]:fade-out-0",
        "data-[exiting]:ease-out-quart",
      ].join(" "),
      description: "Smooth scale animation with elastic spring-like easing",
      icon: "gravity-ui:sparkles",
      name: "Smooth Scale",
    },
    {
      classNames: [
        "data-[entering]:animate-in",
        "data-[entering]:slide-in-from-bottom-4",
        "data-[entering]:fade-in-0",
        "data-[entering]:ease-fluid-out",
        "data-[exiting]:animate-out",
        "data-[exiting]:slide-out-to-bottom-2",
        "data-[exiting]:fade-out-0",
        "data-[exiting]:ease-in-quad",
      ].join(" "),
      description: "Gentle upward slide with seamless fade transition",
      icon: "gravity-ui:arrow-up-from-line",
      name: "Slide Up",
    },
  ];

  return (
    <div className="flex flex-wrap gap-4">
      {animations.map(({classNames, description, icon, name}) => (
        <AlertDialog key={name}>
          <Button variant="secondary">{name}</Button>
          <AlertDialog.Container
            backdropClassName="data-[exiting]:duration-250"
            className={`data-[entering]:duration-300 data-[exiting]:duration-200 ${classNames}`}
          >
            <AlertDialog.Dialog className="sm:max-w-[400px]">
              {({close}) => (
                <>
                  <AlertDialog.Header>
                    <AlertDialog.Icon status="accent">
                      <Icon className="size-5" icon={icon} />
                    </AlertDialog.Icon>
                    <AlertDialog.Heading>{name} Animation</AlertDialog.Heading>
                  </AlertDialog.Header>
                  <AlertDialog.Body>
                    <p className="mt-1">
                      {description}. Customize entrance and exit animations using Tailwind's
                      animation utilities. Combine <code>data-[entering]</code> and{" "}
                      <code>data-[exiting]</code> states with custom timings and easing functions
                      for polished transitions.
                    </p>
                  </AlertDialog.Body>
                  <AlertDialog.Footer>
                    <Button variant="tertiary" onPress={close}>
                      Close
                    </Button>
                    <Button onPress={close}>Try Again</Button>
                  </AlertDialog.Footer>
                </>
              )}
            </AlertDialog.Dialog>
          </AlertDialog.Container>
        </AlertDialog>
      ))}
    </div>
  );
}

Styling

Passing Tailwind CSS classes

import { AlertDialog, Button } from '@heroui/react';

function CustomAlertDialog() {
  return (
    <AlertDialog>
      <Button variant="danger">Delete</Button>
      <AlertDialog.Container
        backdropClassName="bg-red-950/90"
        className="items-start pt-20"
      >
        <AlertDialog.Dialog className="border-red-500 border-2 sm:max-w-[400px]">
          {({close}) => (
            <>
              <AlertDialog.Header>
                <AlertDialog.Icon status="danger" />
                <AlertDialog.Heading>Custom Styled Alert</AlertDialog.Heading>
              </AlertDialog.Header>
              <AlertDialog.Body>
                <p>This alert dialog has custom styling applied via Tailwind classes</p>
              </AlertDialog.Body>
              <AlertDialog.Footer>
                <Button variant="tertiary" onPress={close}>
                  Cancel
                </Button>
                <Button variant="danger" onPress={close}>
                  Delete
                </Button>
              </AlertDialog.Footer>
            </>
          )}
        </AlertDialog.Dialog>
      </AlertDialog.Container>
    </AlertDialog>
  );
}

Customizing the component classes

To customize the AlertDialog component classes, you can use the @layer components directive.
Learn more.

@layer components {
  .alert-dialog__backdrop {
    @apply bg-gradient-to-br from-black/60 to-black/80;
  }

  .alert-dialog__dialog {
    @apply rounded-2xl border border-red-500/20 shadow-2xl;
  }

  .alert-dialog__header {
    @apply gap-4;
  }

  .alert-dialog__icon {
    @apply size-16;
  }

  .alert-dialog__close-trigger {
    @apply rounded-full bg-white/10 hover:bg-white/20;
  }
}

HeroUI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.

CSS Classes

The AlertDialog component uses these CSS classes (View source styles):

Base Classes

  • .alert-dialog__trigger - Trigger element that opens the alert dialog
  • .alert-dialog__backdrop - Overlay backdrop behind the dialog
  • .alert-dialog__container - Positioning wrapper with placement support
  • .alert-dialog__dialog - Dialog content container
  • .alert-dialog__header - Header section for icon and title
  • .alert-dialog__heading - Heading text styles
  • .alert-dialog__body - Main content area
  • .alert-dialog__footer - Footer section for actions
  • .alert-dialog__icon - Icon container with status colors
  • .alert-dialog__close-trigger - Close button element

Backdrop Variants

  • .alert-dialog__backdrop--solid - Solid colored backdrop (default)
  • .alert-dialog__backdrop--blur - Blurred backdrop with glass effect
  • .alert-dialog__backdrop--transparent - Transparent backdrop (no overlay)

Status Variants (Icon)

  • .alert-dialog__icon--default - Default gray status
  • .alert-dialog__icon--accent - Accent blue status
  • .alert-dialog__icon--success - Success green status
  • .alert-dialog__icon--warning - Warning orange status
  • .alert-dialog__icon--danger - Danger red status

Interactive States

The component supports these interactive states:

  • Focus: :focus-visible or [data-focus-visible="true"] - Applied to trigger, dialog, and close button
  • Hover: :hover or [data-hovered="true"] - Applied to close button on hover
  • Active: :active or [data-pressed="true"] - Applied to close button when pressed
  • Entering: [data-entering] - Applied during dialog opening animation
  • Exiting: [data-exiting] - Applied during dialog closing animation
  • Placement: [data-placement="*"] - Applied based on dialog position (auto, top, center, bottom)

API Reference

AlertDialog

PropTypeDefaultDescription
childrenReactNode-Trigger and container elements

AlertDialog.Trigger

PropTypeDefaultDescription
childrenReactNode-Custom trigger content
classNamestring-CSS classes

AlertDialog.Container

PropTypeDefaultDescription
placement"auto" | "center" | "top" | "bottom""auto"Dialog position on screen
backdropVariant"solid" | "blur" | "transparent""solid"Backdrop overlay style
isDismissablebooleanfalseClose on backdrop click
isKeyboardDismissDisabledbooleantrueDisable ESC key to close
isOpenboolean-Controlled open state
onOpenChange(isOpen: boolean) => void-Open state change handler
backdropClassNamestring | (values) => string-Backdrop CSS classes
classNamestring | (values) => string-Container CSS classes

AlertDialog.Dialog

PropTypeDefaultDescription
childrenReactNode | ({close}) => ReactNode-Content or render function
classNamestring-CSS classes
rolestring"alertdialog"ARIA role
aria-labelstring-Accessibility label
aria-labelledbystring-ID of label element
aria-describedbystring-ID of description element

AlertDialog.Header

PropTypeDefaultDescription
childrenReactNode-Header content (typically Icon and Heading)
classNamestring-CSS classes

AlertDialog.Heading

PropTypeDefaultDescription
childrenReactNode-Heading text
classNamestring-CSS classes

AlertDialog.Body

PropTypeDefaultDescription
childrenReactNode-Body content
classNamestring-CSS classes

AlertDialog.Footer

PropTypeDefaultDescription
childrenReactNode-Footer content (typically action buttons)
classNamestring-CSS classes

AlertDialog.Icon

PropTypeDefaultDescription
childrenReactNode-Custom icon element
status"default" | "accent" | "success" | "warning" | "danger""danger"Status color variant
classNamestring-CSS classes

AlertDialog.CloseTrigger

PropTypeDefaultDescription
asChildbooleanfalseRender as child
childrenReactNode-Custom close button
classNamestring | (values) => string-CSS classes

useOverlayState Hook

import { useOverlayState } from '@heroui/react';

const state = useOverlayState({
  defaultOpen: false,
  onOpenChange: (isOpen) => console.log(isOpen)
});

state.isOpen      // Current state
state.open()      // Open dialog
state.close()     // Close dialog
state.toggle()    // Toggle state
state.setOpen()   // Set state directly

Accessibility

Implements WAI-ARIA AlertDialog pattern:

  • Focus trap: Focus locked within alert dialog
  • Keyboard: ESC closes (when enabled), Tab cycles elements
  • Screen readers: Proper ARIA attributes with role="alertdialog"
  • Scroll lock: Body scroll disabled when open
  • Required action: Defaults to requiring explicit user action (no backdrop/ESC dismiss)