Alert dialog
A confirmation dialog for destructive or important actions that waits for async work, and becomes a bottom sheet on phones.
Used in Pro blocks: API keys, Billing, Notifications, and 3 more
pnpm dlx shadcn@latest add https://hextaui.com/r/alert-dialog.jsonAdds the component, the HextaUI theme tokens and any HextaUI components it depends on.
Add the theme tokens to your global CSS, if you haven’t yet.
Install the dependencies.
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cnCopy and paste the following code into your project.
components/ui/alert-dialog.tsx components/ui/sheet.tsx components/ui/button.tsx Update the import paths to match your project setup.
Return a promise from the onClick of <AlertDialogAction />. The button shows a spinner and the dialog can’t be closed until the promise settles. It closes when it resolves and stays open when it rejects, so people can try again.
<AlertDialogMedia /> places an icon beside the title on desktop and above it on phones. Use variant="destructive" for destructive actions.
size="sm" centers the content and lays the buttons out side by side, for short questions.
Create a handle with createAlertDialogHandle() to share one dialog between many triggers. Each trigger passes its own payload, which the dialog reads through a render function.
Pass open and onOpenChange to open it from code, without a trigger. Focus still lands on Cancel.
A dialog opened from inside another stacks on top. The parent scales back while the child is open and comes forward again when it closes.
Content taller than the screen scrolls inside the dialog while the page stays locked.
Pass dir to the content as well, because it renders in a portal outside your RTL container. Arrow keys in the footer follow the reading direction.
| Key | Action |
|---|---|
| EnterSpace | On the trigger, opens the dialog and moves focus to Cancel. |
| TabShift+Tab | Moves focus between controls. Focus stays inside. |
| ←→↑↓ | Moves between the footer buttons and wraps around. Left and right follow the reading direction. |
| Esc | Closes the dialog and returns focus to the trigger. Ignored while an async action is running. |
- The content has
role="alertdialog"and is labelled by its title and described by its description. - Focus starts on
<AlertDialogCancel />, the least destructive choice. PassinitialFocusto change it. - Clicking the backdrop doesn’t close it, so a decision is never dismissed by accident. Set
disablePointerDismissal={false}to allow it. - On phones it becomes a bottom sheet that can be swiped down to cancel. While an action is pending, swiping, Esc and Cancel are blocked and the buttons stay focusable.
- With reduced motion on, it fades instead of scaling.
Built on the Base UI drawer, through <Sheet />. Every part accepts the props of the primitive or element it wraps.
| Prop | Type | Default |
|---|---|---|
open | boolean | – |
defaultOpen | boolean | false |
onOpenChangeNot called for closes blocked by a pending action. | (open: boolean, details) => void | – |
onOpenChangeCompleteCalled after the open or close animation. | (open: boolean) => void | – |
disablePointerDismissalKeeps the dialog open on backdrop clicks. | boolean | true |
handleConnects detached triggers. | AlertDialogHandle<Payload> | – |
actionsRefClose or unmount the dialog imperatively. | RefObject<{ close, unmount }> | – |
children | ReactNode | ({ payload }) => ReactNode | – |
| Prop | Type | Default |
|---|---|---|
handle | AlertDialogHandle<Payload> | – |
payloadPassed to the dialog’s render function. | Payload | – |
render | ReactElement | (props, state) => ReactElement | <button> |
| Attribute | Description |
|---|---|
data-slot="alert-dialog-trigger" | Target triggers in CSS. |
data-popup-open | Present while its dialog is open. |
| Prop | Type | Default |
|---|---|---|
size | "default" | "sm" | "default" |
initialFocus | boolean | RefObject | (openType) => HTMLElement | boolean | Cancel button |
finalFocus | boolean | RefObject | (closeType) => HTMLElement | boolean | The trigger |
dirSet it when the dialog should be right to left. | "ltr" | "rtl" | – |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribute | Description |
|---|---|
data-slot="alert-dialog-content" | Target the dialog in CSS. |
data-size | The current size. |
data-open | Present while open. |
data-starting-style | Present while the dialog animates in. |
data-ending-style | Present while the dialog animates out. |
data-nested-drawer-open | Present while a nested dialog is open on top. |
data-swiping | Present while it is being swiped on a phone. |
--nested-drawers | How many dialogs are open on top of this one. |
A <div> that stacks the media, title and description.
| Attribute | Description |
|---|---|
data-slot="alert-dialog-header" | Target the header in CSS. |
| Prop | Type | Default |
|---|---|---|
variant | "default" | "destructive" | "default" |
| Attribute | Description |
|---|---|
data-slot="alert-dialog-media" | Target the media in CSS. |
| Prop | Type | Default |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <h2> |
| Attribute | Description |
|---|---|
data-slot="alert-dialog-title" | Target the title in CSS. |
| Prop | Type | Default |
|---|---|---|
renderUse render={<div />} when it holds several paragraphs. | ReactElement | (props, state) => ReactElement | <p> |
| Attribute | Description |
|---|---|
data-slot="alert-dialog-description" | Target the description in CSS. |
A <div> for the buttons. Buttons stack full width on phones, and arrow keys move between them.
| Attribute | Description |
|---|---|
data-slot="alert-dialog-footer" | Target the footer in CSS. |
A <Button /> that closes the dialog when clicked. It accepts every Button prop.
| Prop | Type | Default |
|---|---|---|
onClickReturn a promise to show a spinner and keep the dialog open until it settles. Call event.preventDefault() to keep it open. | (event) => void | PromiseLike<unknown> | – |
variant | ButtonVariant | "default" |
| Attribute | Description |
|---|---|
data-slot="alert-dialog-action" | Target actions in CSS. |
| Prop | Type | Default |
|---|---|---|
variant | ButtonVariant | "outline" |
size | ButtonSize | – |
disabledAlso disabled while an action is pending. | boolean | – |
| Attribute | Description |
|---|---|
data-slot="alert-dialog-cancel" | Target the cancel button in CSS. It receives initial focus. |
data-disabled | Present while disabled. |
<AlertDialogContent /> already renders both. Use them only when composing a custom popup.
| Prop | Type | Default |
|---|---|---|
keepMountedOn the portal, keeps the dialog in the DOM while closed. | boolean | false |
Returns a handle that connects <AlertDialogTrigger /> elements anywhere on the page to one <AlertDialog />. Type the payload with a generic: createAlertDialogHandle<{ name: string }>().
- ButtonButtons in every variant and size, with a built-in loading, success and error flow that skips the spinner for fast requests.
- SheetA panel that slides in from any edge, with swipe-to-dismiss, scroll lock and stacked nesting.
- CommandA searchable list of actions, inline or as a ⌘K palette, with pages, shortcuts and highlighted matches.
- Context menuA menu of actions on right click or long press, with submenus, checkbox and radio items, and hold feedback on touch.
- DialogA window over the page for forms and focused tasks, with a pinned header and footer, nesting, and a swipeable bottom sheet on phones.
- DrawerA panel that slides in from any edge and follows your finger, with snap points, a working handle and nested drawers that stack.
Used in Pro blocks
Blocks that build on Alert dialog.
- API keysThe API keys page of an AI product. Handles a secret shown only once, revoke with undo and rotation with a grace period.
- BillingThe plan and usage page of an AI product. Handles credits running out mid-cycle, plan changes with proration and card errors.
- NotificationsThe Notifications section of an AI product’s settings. Handles a channel by event grid, quiet hours and browser permission for push.
- SecurityThe Security section of an AI product’s settings. Handles sessions, two-factor with recovery codes and deletion behind a typed confirmation.
- SettingsThe settings page of an AI product. Handles a save bar that appears only when something changed, field errors and loading states.
- TeamThe team page of an AI product’s workspace settings. Handles seats running out, pending invites and ownership transfer with a typed confirmation.
Every block comes with HextaUI Pro, and Prompt Input is free.