Skip to main content

Modal dialog for critical confirmations requiring user attention and explicit action

Import

import { AlertDialog } from "heroui-solid";

Usage

import { AlertDialog, Button } from "heroui-solid"

export function Default() {
  return (
    <AlertDialog>
      <Button variant="danger">Delete Project</Button>
      <AlertDialog.Backdrop>
        <AlertDialog.Container>
          <AlertDialog.Dialog class="sm:max-w-[400px]">
            <AlertDialog.CloseTrigger />
            <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 slot="close" variant="tertiary">
                Cancel
              </Button>
              <Button slot="close" variant="danger">
                Delete Project
              </Button>
            </AlertDialog.Footer>
          </AlertDialog.Dialog>
        </AlertDialog.Container>
      </AlertDialog.Backdrop>
    </AlertDialog>
  )
}

Anatomy

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

import { AlertDialog, Button } from "heroui-solid";

export default () => (
  <AlertDialog>
    <Button>Open Alert Dialog</Button>
    <AlertDialog.Backdrop>
      <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.Backdrop>
  </AlertDialog>
);

Statuses

import { AlertDialog, Button } from "heroui-solid"

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 class="flex flex-wrap gap-4">
      {examples.map(
        ({ actions, body, classNames, header, status, trigger }) => (
          <AlertDialog>
            <Button class={classNames}>{trigger}</Button>
            <AlertDialog.Backdrop>
              <AlertDialog.Container>
                <AlertDialog.Dialog class="sm:max-w-[400px]">
                  <AlertDialog.CloseTrigger />
                  <AlertDialog.Header>
                    <AlertDialog.Icon status={status} />
                    <AlertDialog.Heading>{header}</AlertDialog.Heading>
                  </AlertDialog.Header>
                  <AlertDialog.Body>
                    <p>{body}</p>
                  </AlertDialog.Body>
                  <AlertDialog.Footer>
                    <Button slot="close" variant="tertiary">
                      {actions.cancel}
                    </Button>
                    <Button
                      slot="close"
                      variant={status === "danger" ? "danger" : "primary"}
                    >
                      {actions.confirm}
                    </Button>
                  </AlertDialog.Footer>
                </AlertDialog.Dialog>
              </AlertDialog.Container>
            </AlertDialog.Backdrop>
          </AlertDialog>
        )
      )}
    </div>
  )
}

Placements

import { AlertDialog, Button } from "heroui-solid"

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

  return (
    <div class="flex flex-wrap gap-4">
      {placements.map((placement) => (
        <AlertDialog>
          <Button variant="secondary">
            {placement.charAt(0).toUpperCase() + placement.slice(1)}
          </Button>
          <AlertDialog.Backdrop>
            <AlertDialog.Container placement={placement}>
              <AlertDialog.Dialog class="sm:max-w-[400px]">
                <AlertDialog.CloseTrigger />
                <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 slot="close" variant="tertiary">
                    Cancel
                  </Button>
                  <Button slot="close">Confirm</Button>
                </AlertDialog.Footer>
              </AlertDialog.Dialog>
            </AlertDialog.Container>
          </AlertDialog.Backdrop>
        </AlertDialog>
      ))}
    </div>
  )
}

Backdrop Variants

import { AlertDialog, Button } from "heroui-solid"

export function BackdropVariants() {
  const variants = ["opaque", "blur", "transparent"] as const

  return (
    <div class="flex flex-wrap gap-4">
      {variants.map((variant) => (
        <AlertDialog>
          <Button variant="secondary">
            {variant.charAt(0).toUpperCase() + variant.slice(1)}
          </Button>
          <AlertDialog.Backdrop variant={variant}>
            <AlertDialog.Container>
              <AlertDialog.Dialog class="sm:max-w-[400px]">
                <AlertDialog.CloseTrigger />
                <AlertDialog.Header>
                  <AlertDialog.Icon status="accent" />
                  <AlertDialog.Heading>
                    Backdrop:{" "}
                    {variant.charAt(0).toUpperCase() + variant.slice(1)}
                  </AlertDialog.Heading>
                </AlertDialog.Header>
                <AlertDialog.Body>
                  <p>
                    {variant === "opaque"
                      ? "An opaque dark backdrop that completely obscures the background, providing maximum focus on the dialog."
                      : variant === "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 slot="close" variant="tertiary">
                    Cancel
                  </Button>
                  <Button slot="close">Confirm</Button>
                </AlertDialog.Footer>
              </AlertDialog.Dialog>
            </AlertDialog.Container>
          </AlertDialog.Backdrop>
        </AlertDialog>
      ))}
    </div>
  )
}

Sizes

import { Rocket } from "gravity-icons-solid"
import { AlertDialog, Button } from "heroui-solid"

export function Sizes() {
  const sizes = ["xs", "sm", "md", "lg", "cover"] as const

  return (
    <div class="flex flex-wrap gap-4">
      {sizes.map((size) => (
        <AlertDialog>
          <Button variant="secondary">
            {size.charAt(0).toUpperCase() + size.slice(1)}
          </Button>
          <AlertDialog.Backdrop>
            <AlertDialog.Container size={size}>
              <AlertDialog.Dialog>
                <AlertDialog.CloseTrigger />
                <AlertDialog.Header>
                  <AlertDialog.Icon class="bg-default text-foreground">
                    <Rocket class="size-5" />
                  </AlertDialog.Icon>
                  <AlertDialog.Heading>
                    Size: {size.charAt(0).toUpperCase() + size.slice(1)}
                  </AlertDialog.Heading>
                </AlertDialog.Header>
                <AlertDialog.Body>
                  <p>
                    {size === "cover" ? (
                      <>
                        This alert dialog uses the <code>cover</code> size
                        variant. It spans the full screen with margins: 16px on
                        mobile and 40px on desktop. Maintains rounded corners
                        and standard padding. Perfect for critical confirmations
                        that need maximum width while preserving alert dialog
                        aesthetics.
                      </>
                    ) : (
                      <>
                        This alert dialog uses the <code>{size}</code> size
                        variant. On mobile devices, all sizes adapt to near
                        full-width for optimal viewing. On desktop, each size
                        provides a different maximum width to suit various
                        content needs.
                      </>
                    )}
                  </p>
                </AlertDialog.Body>
                <AlertDialog.Footer>
                  <Button slot="close" variant="tertiary">
                    Cancel
                  </Button>
                  <Button slot="close">Confirm</Button>
                </AlertDialog.Footer>
              </AlertDialog.Dialog>
            </AlertDialog.Container>
          </AlertDialog.Backdrop>
        </AlertDialog>
      ))}
    </div>
  )
}

Custom Icon

import { LockOpen } from "gravity-icons-solid"
import { AlertDialog, Button } from "heroui-solid"

export function CustomIcon() {
  return (
    <AlertDialog>
      <Button variant="secondary">Reset Password</Button>
      <AlertDialog.Backdrop>
        <AlertDialog.Container>
          <AlertDialog.Dialog class="sm:max-w-[400px]">
            <AlertDialog.CloseTrigger />
            <AlertDialog.Header>
              <AlertDialog.Icon status="warning">
                <LockOpen class="size-5" />
              </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 slot="close" variant="tertiary">
                Cancel
              </Button>
              <Button slot="close">Send Reset Link</Button>
            </AlertDialog.Footer>
          </AlertDialog.Dialog>
        </AlertDialog.Container>
      </AlertDialog.Backdrop>
    </AlertDialog>
  )
}

Custom Backdrop

import { TriangleExclamation } from "gravity-icons-solid"
import { AlertDialog, Button } from "heroui-solid"

export function CustomBackdrop() {
  return (
    <AlertDialog>
      <Button variant="danger">Delete Account</Button>
      <AlertDialog.Backdrop
        class="bg-linear-to-t from-red-950/90 via-red-950/50 to-transparent dark:from-red-950/95 dark:via-red-950/60"
        variant="blur"
      >
        <AlertDialog.Container>
          <AlertDialog.Dialog class="sm:max-w-[420px]">
            <AlertDialog.CloseTrigger />
            <AlertDialog.Header class="items-center text-center">
              <AlertDialog.Icon status="danger">
                <TriangleExclamation class="size-5" />
              </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 class="flex-col-reverse">
              <Button class="w-full" slot="close">
                Keep Account
              </Button>
              <Button class="w-full" slot="close" variant="danger">
                Delete Forever
              </Button>
            </AlertDialog.Footer>
          </AlertDialog.Dialog>
        </AlertDialog.Container>
      </AlertDialog.Backdrop>
    </AlertDialog>
  )
}

Dismiss Behavior

isDismissable

Controls whether the alert dialog can be dismissed by clicking the overlay backdrop. Alert dialogs typically require explicit action, so this defaults to false. Set to true for less critical confirmations.

isKeyboardDismissDisabled

Controls whether the ESC key can dismiss the alert dialog. Alert dialogs typically require explicit action, so this defaults to true. When set to false, the ESC key will be enabled.

import { CircleInfo } from "gravity-icons-solid"
import { AlertDialog, Button } from "heroui-solid"

export function DismissBehavior() {
  return (
    <div class="flex max-w-sm flex-col gap-6">
      <div class="flex flex-col gap-2">
        <h3 class="text-lg font-semibold">isDismissable</h3>
        <p class="text-sm text-muted">
          Controls whether the alert dialog can be dismissed by clicking the
          overlay backdrop. Alert dialogs typically require explicit action, so
          this defaults to <code>false</code>. Set to <code>true</code> for less
          critical confirmations.
        </p>
        <AlertDialog>
          <Button variant="secondary">Open Alert Dialog</Button>
          <AlertDialog.Backdrop isDismissable={false}>
            <AlertDialog.Container>
              <AlertDialog.Dialog class="sm:max-w-[400px]">
                <AlertDialog.CloseTrigger />
                <AlertDialog.Header>
                  <AlertDialog.Icon status="danger">
                    <CircleInfo class="size-5" />
                  </AlertDialog.Icon>
                  <AlertDialog.Heading>
                    isDismissable = false
                  </AlertDialog.Heading>
                  <p class="text-sm leading-5 text-muted">
                    Clicking the backdrop won't close this alert dialog
                  </p>
                </AlertDialog.Header>
                <AlertDialog.Body>
                  <p>
                    Try clicking outside this alert dialog on the overlay - it
                    won't close. You must use the action buttons to dismiss it.
                  </p>
                </AlertDialog.Body>
                <AlertDialog.Footer>
                  <Button slot="close" variant="tertiary">
                    Cancel
                  </Button>
                  <Button slot="close">Confirm</Button>
                </AlertDialog.Footer>
              </AlertDialog.Dialog>
            </AlertDialog.Container>
          </AlertDialog.Backdrop>
        </AlertDialog>
      </div>

      <div class="flex flex-col gap-2">
        <h3 class="text-lg font-semibold">isKeyboardDismissDisabled</h3>
        <p class="text-sm text-muted">
          Controls whether the ESC key can dismiss the alert dialog. Alert
          dialogs typically require explicit action, so this defaults to{" "}
          <code>true</code>. When set to <code>false</code>, the ESC key will be
          enabled.
        </p>
        <AlertDialog>
          <Button variant="secondary">Open Alert Dialog</Button>
          <AlertDialog.Backdrop isKeyboardDismissDisabled>
            <AlertDialog.Container>
              <AlertDialog.Dialog class="sm:max-w-[400px]">
                <AlertDialog.CloseTrigger />
                <AlertDialog.Header>
                  <AlertDialog.Icon status="accent">
                    <CircleInfo class="size-5" />
                  </AlertDialog.Icon>
                  <AlertDialog.Heading>
                    isKeyboardDismissDisabled = true
                  </AlertDialog.Heading>
                  <p class="text-sm leading-5 text-muted">
                    ESC key is disabled
                  </p>
                </AlertDialog.Header>
                <AlertDialog.Body>
                  <p>
                    Press ESC - nothing happens. You must use the action buttons
                    to dismiss this alert dialog.
                  </p>
                </AlertDialog.Body>
                <AlertDialog.Footer>
                  <Button slot="close" variant="tertiary">
                    Cancel
                  </Button>
                  <Button slot="close">Confirm</Button>
                </AlertDialog.Footer>
              </AlertDialog.Dialog>
            </AlertDialog.Container>
          </AlertDialog.Backdrop>
        </AlertDialog>
      </div>
    </div>
  )
}

Close Methods

Using slot="close"

The simplest way to close a dialog. Add slot="close" to any Button component within the dialog. When clicked, it will automatically close the dialog.

Using Dialog render props

Access the close method from the Dialog's render props. This gives you full control over when and how to close the dialog, allowing you to add custom logic before closing.

import { AlertDialog, Button } from "heroui-solid"

export function CloseMethods() {
  return (
    <div class="flex max-w-2xl flex-col gap-8">
      <div class="flex flex-col gap-2">
        <h3 class="text-lg font-semibold">Using slot="close"</h3>
        <p class="text-sm text-muted">
          The simplest way to close a dialog. Add <code>slot="close"</code> to
          any Button component within the dialog. When clicked, it will
          automatically close the dialog.
        </p>
        <AlertDialog>
          <Button variant="secondary">Open Dialog</Button>
          <AlertDialog.Backdrop>
            <AlertDialog.Container>
              <AlertDialog.Dialog class="sm:max-w-[400px]">
                <AlertDialog.Header>
                  <AlertDialog.Icon status="accent" />
                  <AlertDialog.Heading>Using slot="close"</AlertDialog.Heading>
                </AlertDialog.Header>
                <AlertDialog.Body>
                  <p>
                    Click either button below - both have{" "}
                    <code>slot="close"</code> and will close the dialog
                    automatically.
                  </p>
                </AlertDialog.Body>
                <AlertDialog.Footer>
                  <Button slot="close" variant="tertiary">
                    Cancel
                  </Button>
                  <Button slot="close">Confirm</Button>
                </AlertDialog.Footer>
              </AlertDialog.Dialog>
            </AlertDialog.Container>
          </AlertDialog.Backdrop>
        </AlertDialog>
      </div>

      <div class="flex flex-col gap-2">
        <h3 class="text-lg font-semibold">Using Dialog render props</h3>
        <p class="text-sm text-muted">
          Access the <code>close</code> method from the Dialog's render props.
          This gives you full control over when and how to close the dialog,
          allowing you to add custom logic before closing.
        </p>
        <AlertDialog>
          <Button variant="secondary">Open Dialog</Button>
          <AlertDialog.Backdrop>
            <AlertDialog.Container>
              <AlertDialog.Dialog class="sm:max-w-[400px]">
                {(renderProps) => (
                  <>
                    <AlertDialog.Header>
                      <AlertDialog.Icon status="success" />
                      <AlertDialog.Heading>
                        Using Dialog render props
                      </AlertDialog.Heading>
                    </AlertDialog.Header>
                    <AlertDialog.Body>
                      <p>
                        The buttons below use the <code>close</code> method from
                        render props. You can add validation or other logic
                        before calling <code>renderProps.close()</code>.
                      </p>
                    </AlertDialog.Body>
                    <AlertDialog.Footer>
                      <Button
                        variant="tertiary"
                        onClick={() => renderProps.close()}
                      >
                        Cancel
                      </Button>
                      <Button onClick={() => renderProps.close()}>
                        Confirm
                      </Button>
                    </AlertDialog.Footer>
                  </>
                )}
              </AlertDialog.Dialog>
            </AlertDialog.Container>
          </AlertDialog.Backdrop>
        </AlertDialog>
      </div>
    </div>
  )
}

Controlled State

With createSignal()

Control the alert dialog using Solid's createSignal 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

import { AlertDialog, Button, useOverlayState } from "heroui-solid"
import { createSignal } from "solid-js"

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

  const state = useOverlayState()

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

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

      <div class="flex flex-col gap-3">
        <h3 class="text-lg font-semibold text-foreground">
          With useOverlayState()
        </h3>
        <p class="text-sm leading-relaxed text-pretty text-muted">
          Use the <code class="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 class="flex flex-col items-start gap-3 rounded-2xl bg-surface p-4 shadow-sm">
          <div class="flex w-full items-center justify-between">
            <p class="text-xs text-muted">
              Status:{" "}
              <span class="font-mono font-medium text-foreground">
                {state.isOpen ? "open" : "closed"}
              </span>
            </p>
          </div>
          <div class="flex gap-2">
            <Button size="sm" variant="secondary" onClick={state.open}>
              Open Dialog
            </Button>
            <Button size="sm" variant="tertiary" onClick={state.toggle}>
              Toggle
            </Button>
          </div>
        </div>

        <AlertDialog.Backdrop
          isOpen={state.isOpen}
          onOpenChange={state.setOpen}
        >
          <AlertDialog.Container>
            <AlertDialog.Dialog class="sm:max-w-[400px]">
              <AlertDialog.CloseTrigger />
              <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 slot="close" variant="tertiary">
                  Cancel
                </Button>
                <Button slot="close">Confirm</Button>
              </AlertDialog.Footer>
            </AlertDialog.Dialog>
          </AlertDialog.Container>
        </AlertDialog.Backdrop>
      </div>
    </div>
  )
}

Custom Trigger

Delete Item

Permanently remove this item

import { TrashBin } from "gravity-icons-solid"
import { AlertDialog, Button } from "heroui-solid"

export function CustomTrigger() {
  return (
    <AlertDialog>
      <AlertDialog.Trigger class="group flex items-center gap-3 rounded-2xl bg-surface p-4 shadow-xs select-none hover:bg-surface-secondary">
        <div class="flex size-12 shrink-0 items-center justify-center rounded-xl bg-danger-soft text-danger-soft-foreground">
          <TrashBin class="size-6" />
        </div>
        <div class="flex flex-1 flex-col gap-0.5">
          <p class="text-sm font-semibold">Delete Item</p>
          <p class="text-xs text-muted">Permanently remove this item</p>
        </div>
      </AlertDialog.Trigger>
      <AlertDialog.Backdrop>
        <AlertDialog.Container>
          <AlertDialog.Dialog class="sm:max-w-[400px]">
            <AlertDialog.CloseTrigger />
            <AlertDialog.Header>
              <AlertDialog.Icon status="danger">
                <TrashBin class="size-5" />
              </AlertDialog.Icon>
              <AlertDialog.Heading>Delete this item?</AlertDialog.Heading>
            </AlertDialog.Header>
            <AlertDialog.Body>
              <p>
                Use <code>AlertDialog.Trigger</code> to create custom trigger
                elements beyond standard buttons. This example shows a
                card-style trigger with icons and descriptive text.
              </p>
            </AlertDialog.Body>
            <AlertDialog.Footer>
              <Button slot="close" variant="tertiary">
                Cancel
              </Button>
              <Button slot="close" variant="danger">
                Delete Item
              </Button>
            </AlertDialog.Footer>
          </AlertDialog.Dialog>
        </AlertDialog.Container>
      </AlertDialog.Backdrop>
    </AlertDialog>
  )
}

Custom Animations

import { ArrowUpFromLine, Sparkles } from "gravity-icons-solid"
import { AlertDialog, Button } from "heroui-solid"
import type { Component } from "solid-js"

const iconMap: Record<string, Component<{ class?: string }>> = {
  "gravity-ui:arrow-up-from-line": ArrowUpFromLine,
  "gravity-ui:sparkles": Sparkles
}

export function CustomAnimations() {
  const animations = [
    {
      classNames: {
        backdrop: [
          "data-[expanded]:duration-400",
          "data-[expanded]:ease-[cubic-bezier(0.16,1,0.3,1)]",
          "data-[closed]:duration-200",
          "data-[closed]:ease-[cubic-bezier(0.7,0,0.84,0)]"
        ].join(" "),
        container: [
          "data-[expanded]:animate-in",
          "data-[expanded]:fade-in-0",
          "data-[expanded]:zoom-in-95",
          "data-[expanded]:duration-400",
          "data-[expanded]:ease-[cubic-bezier(0.16,1,0.3,1)]",
          "data-[closed]:animate-out",
          "data-[closed]:fade-out-0",
          "data-[closed]:zoom-out-95",
          "data-[closed]:duration-200",
          "data-[closed]:ease-[cubic-bezier(0.7,0,0.84,0)]"
        ].join(" ")
      },
      description:
        "Physics-based elastic scaling. Simulates a high-damping spring system with fast transient response and prolonged settling time. Ideal for Alert Dialogs and Modals.",
      icon: "gravity-ui:sparkles",
      name: "Kinematic Scale"
    },
    {
      classNames: {
        backdrop: [
          "data-[expanded]:duration-500",
          "data-[expanded]:ease-[cubic-bezier(0.25,1,0.5,1)]",
          "data-[closed]:duration-200",
          "data-[closed]:ease-[cubic-bezier(0.5,0,0.75,0)]"
        ].join(" "),
        container: [
          "data-[expanded]:animate-in",
          "data-[expanded]:fade-in-0",
          "data-[expanded]:slide-in-from-bottom-4",
          "data-[expanded]:duration-500",
          "data-[expanded]:ease-[cubic-bezier(0.25,1,0.5,1)]",
          "data-[closed]:animate-out",
          "data-[closed]:fade-out-0",
          "data-[closed]:slide-out-to-bottom-2",
          "data-[closed]:duration-200",
          "data-[closed]:ease-[cubic-bezier(0.5,0,0.75,0)]"
        ].join(" ")
      },
      description:
        "Simulates movement through a medium with fluid resistance. Eliminates mechanical linearity for a natural, grounded feel. Perfect for Bottom Sheets or Toasts.",
      icon: "gravity-ui:arrow-up-from-line",
      name: "Fluid Slide"
    }
  ]

  return (
    <div class="flex flex-wrap gap-4">
      {animations.map(({ classNames, description, icon, name }) => {
        const IconComponent = iconMap[icon]

        return (
          <AlertDialog>
            <Button variant="secondary">{name}</Button>
            <AlertDialog.Backdrop class={classNames.backdrop}>
              <AlertDialog.Container class={classNames.container}>
                <AlertDialog.Dialog class="sm:max-w-[400px]">
                  <AlertDialog.CloseTrigger />
                  <AlertDialog.Header>
                    <AlertDialog.Icon status="accent">
                      {!!IconComponent && <IconComponent class="size-5" />}
                    </AlertDialog.Icon>
                    <AlertDialog.Heading>{name} Animation</AlertDialog.Heading>
                  </AlertDialog.Header>
                  <AlertDialog.Body>
                    <p class="mt-1">{description}</p>
                  </AlertDialog.Body>
                  <AlertDialog.Footer>
                    <Button slot="close" variant="tertiary">
                      Close
                    </Button>
                    <Button slot="close">Try Again</Button>
                  </AlertDialog.Footer>
                </AlertDialog.Dialog>
              </AlertDialog.Container>
            </AlertDialog.Backdrop>
          </AlertDialog>
        )
      })}
    </div>
  )
}

Custom Portal

Render alert dialogs inside a custom container instead of document.body

Apply transform: translateZ(0) to the container to create a new stacking context.

import { AlertDialog, Button } from "heroui-solid"
import { createSignal, onMount, Show } from "solid-js"

export function CustomPortal() {
  const [portalContainer, setPortalContainer] =
    createSignal<HTMLElement | null>(null)
  let containerRef: HTMLDivElement | undefined
  // Publish the container only after mount — setting the signal from the ref
  // during hydration would flip the Show open and desync (see AGENTS.md).
  onMount(() => setPortalContainer(containerRef ?? null))

  return (
    <div class="flex flex-col gap-4">
      <div>
        <p class="text-sm">
          Render alert dialogs inside a custom container instead of{" "}
          <code>document.body</code>
        </p>
        <p class="text-sm text-muted">
          Apply{" "}
          <code class="rounded px-1 py-0.5 text-xs">
            transform: translateZ(0)
          </code>{" "}
          to the container to create a new stacking context.
        </p>
      </div>
      <div
        ref={containerRef}
        class="relative flex h-[380px] items-center justify-center overflow-hidden rounded bg-muted/20"
        // new stacking context
        style={{ transform: "translateZ(0)" }}
      >
        <Show when={portalContainer()}>
          <AlertDialog>
            <Button>Open Alert Dialog</Button>
            <AlertDialog.Backdrop
              class="h-full"
              UNSTABLE_portalContainer={portalContainer()!}
            >
              <AlertDialog.Container class="h-full max-h-full">
                <AlertDialog.Dialog class="h-full max-h-full sm:max-w-md">
                  <AlertDialog.CloseTrigger />
                  <AlertDialog.Header>
                    <AlertDialog.Icon status="accent" />
                    <AlertDialog.Heading>Custom Portal</AlertDialog.Heading>
                  </AlertDialog.Header>
                  <AlertDialog.Body>
                    <p class="text-sm text-muted">
                      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
                      Sed do eiusmod tempor incididunt ut labore et dolore magna
                      aliqua. Ut enim ad minim veniam, quis nostrud exercitation
                      ullamco laboris nisi ut aliquip ex ea commodo consequat.
                    </p>
                    <p class="text-sm text-muted">
                      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
                      Sed do eiusmod tempor incididunt ut labore et dolore magna
                      aliqua. Ut enim ad minim veniam, quis nostrud exercitation
                      ullamco laboris nisi ut aliquip ex ea commodo consequat.
                    </p>
                    <p class="text-sm text-muted">
                      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
                      Sed do eiusmod tempor incididunt ut labore et dolore magna
                      aliqua. Ut enim ad minim veniam, quis nostrud exercitation
                      ullamco laboris nisi ut aliquip ex ea commodo consequat.
                    </p>
                  </AlertDialog.Body>
                  <AlertDialog.Footer>
                    <Button slot="close" variant="tertiary">
                      Cancel
                    </Button>
                    <Button slot="close">Confirm</Button>
                  </AlertDialog.Footer>
                </AlertDialog.Dialog>
              </AlertDialog.Container>
            </AlertDialog.Backdrop>
          </AlertDialog>
        </Show>
      </div>
    </div>
  )
}
  • Button: Allows a user to perform an action
  • CloseButton: Button for dismissing overlays

Styling

Passing Tailwind CSS classes

import { AlertDialog, Button } from "heroui-solid";

function CustomAlertDialog() {
  return (
    <AlertDialog>
      <Button variant="danger">Delete</Button>
      <AlertDialog.Backdrop class="bg-red-950/90">
        <AlertDialog.Container class="items-start pt-20">
          <AlertDialog.Dialog class="border-2 border-red-500 sm:max-w-[400px]">
            <AlertDialog.CloseTrigger />
            <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 slot="close" variant="tertiary">
                Cancel
              </Button>
              <Button slot="close" variant="danger">
                Delete
              </Button>
            </AlertDialog.Footer>
          </AlertDialog.Dialog>
        </AlertDialog.Container>
      </AlertDialog.Backdrop>
    </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--opaque - Opaque 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-expanded] - Applied during dialog opening animation
  • Exiting: [data-closed] - Applied during dialog closing animation
  • Placement: [data-placement="*"] - Applied based on dialog position (auto, top, center, bottom)

API Reference

AlertDialog

PropTypeDefaultDescription
childrenJSX.Element-Trigger and container elements
isOpenboolean-Controlled open state
defaultOpenboolean-Uncontrolled initial open state
onOpenChange(isOpen: boolean) => void-Open state change handler

AlertDialog.Trigger

PropTypeDefaultDescription
childrenJSX.Element-Custom trigger content
classstring-CSS classes
asValidComponent"div"Overrides the default DOM element (Kobalte polymorphic)

AlertDialog.Backdrop

PropTypeDefaultDescription
variant"opaque" | "blur" | "transparent""opaque"Backdrop overlay style
isDismissablebooleanfalseClose on backdrop click
isKeyboardDismissDisabledbooleantrueDisable ESC key to close
isOpenboolean-Controlled open state
onOpenChange(isOpen: boolean) => void-Open state change handler
classstring-Backdrop CSS classes
UNSTABLE_portalContainerNode-Custom portal container

AlertDialog.Container

PropTypeDefaultDescription
placement"auto" | "center" | "top" | "bottom""auto"Dialog position on screen
size"xs" | "sm" | "md" | "lg" | "cover""md"Alert Dialog size variant
classstring-Container CSS classes

AlertDialog.Dialog

PropTypeDefaultDescription
childrenJSX.Element | ({close}) => JSX.Element-Content or render function
classstring-CSS classes

AlertDialog.Header

PropTypeDefaultDescription
childrenJSX.Element-Header content (typically Icon and Heading)
classstring-CSS classes

AlertDialog.Heading

PropTypeDefaultDescription
childrenJSX.Element-Heading text
classstring-CSS classes

AlertDialog.Body

PropTypeDefaultDescription
childrenJSX.Element-Body content
classstring-CSS classes

AlertDialog.Footer

PropTypeDefaultDescription
childrenJSX.Element-Footer content (typically action buttons)
classstring-CSS classes

AlertDialog.Icon

PropTypeDefaultDescription
childrenJSX.Element-Custom icon element
status"default" | "accent" | "success" | "warning" | "danger""danger"Status color variant
classstring-CSS classes

AlertDialog.CloseTrigger

PropTypeDefaultDescription
childrenJSX.Element-Custom close button
classstring-CSS classes

useOverlayState Hook

import { useOverlayState } from "heroui-solid";

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(true); // 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)

Differences from HeroUI React

  • Use class instead of className, and onClick instead of onPress; the element type is overridden with Kobalte's polymorphic as prop instead of React's render prop.
  • AlertDialog.Dialog render-prop children receive { close } directly ({({ close }) => …}) rather than React Aria's full render-props object.
  • Control the dialog with Solid's createSignal (or useOverlayState) instead of React's useState.

Last updated: 7/19/26, 3:27 AM

HeroUI SolidUnofficial SolidJS port of HeroUI v3, built on Kobalte and @heroui/styles