Alert Dialog
A dialog that interrupts the user to confirm an irreversible or consequential action.
function AlertDialogDemo() {
const [open, setOpen] = React.useState(false)
return (
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogTrigger render={<Button variant="destructive" />}>
Delete rule
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete detection rule?</AlertDialogTitle>
<AlertDialogDescription>
“Impossible travel” stops raising alerts in every
tenant. Alerts it already raised stay in the queue.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
variant="destructive"
onClick={() => setOpen(false)}
>
Delete rule
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}Usage
import * as React from "react"
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@zenoxai/ui/components/alert-dialog"
import { Button } from "@zenoxai/ui/components/button"
function DeleteRuleButton() {
const [open, setOpen] = React.useState(false)
return (
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogTrigger render={<Button variant="destructive" />}>
Delete rule
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete detection rule?</AlertDialogTitle>
<AlertDialogDescription>
It stops raising alerts in every tenant.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
variant="destructive"
onClick={async () => {
await deleteRule()
setOpen(false)
}}
>
Delete rule
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}
Use an alert dialog before an action the user cannot undo or would not expect: deleting a rule, revoking a key, archiving alerts in bulk. It asks one question and offers two answers. Unlike Dialog, a click outside does not close it: the user has to choose.
- For a task with fields (rename, create, edit), use Dialog.
- For an action that is easy to undo, skip the confirmation: run it and offer “Undo” in a toast.
- To ask for confirmation from an event handler with one call (
await confirm({...})), use the Confirm Dialog pattern, which is built on this component.
AlertDialogCancel closes the dialog. AlertDialogAction is a plain Button: it runs your
onClick and does not close the dialog, so control open and close it when the action is done.
Write the title as the question (“Delete detection rule?”) and repeat the verb on the action
button (“Delete rule”), never “OK” or “Yes”.
Examples
With an icon
AlertDialogMedia holds an icon above the title, or beside it from the sm breakpoint up.
function AlertDialogWithMedia() {
const [open, setOpen] = React.useState(false)
return (
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogTrigger render={<Button variant="outline" />}>
Revoke API key
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogMedia>
<KeyRoundIcon />
</AlertDialogMedia>
<AlertDialogTitle>Revoke API key?</AlertDialogTitle>
<AlertDialogDescription>
Integrations that use “siem-export” stop sending events
immediately. You cannot restore a revoked key.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Keep key</AlertDialogCancel>
<AlertDialogAction
variant="destructive"
onClick={() => setOpen(false)}
>
Revoke key
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}Small
size="sm" narrows the dialog, centers the text and splits the footer into two equal buttons.
Use it for one-line questions.
function AlertDialogSmall() {
const [open, setOpen] = React.useState(false)
return (
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogTrigger render={<Button variant="outline" />}>
Sign out everywhere
</AlertDialogTrigger>
<AlertDialogContent size="sm">
<AlertDialogHeader>
<AlertDialogTitle>Sign out of all sessions?</AlertDialogTitle>
<AlertDialogDescription>
You will need to sign in again on every device.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={() => setOpen(false)}>
Sign out
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}Pending action
When the action calls the server, keep the dialog open until the request finishes. Disable both
buttons, show a Spinner on the action, and ignore close requests in onOpenChange while it
runs.
function AlertDialogPending() {
const [open, setOpen] = React.useState(false)
const [pending, setPending] = React.useState(false)
function archive() {
setPending(true)
// Stands in for the request that archives the alerts.
setTimeout(() => {
setPending(false)
setOpen(false)
}, 1200)
}
return (
<AlertDialog
open={open}
onOpenChange={(next) => {
if (!pending) setOpen(next)
}}
>
<AlertDialogTrigger render={<Button variant="outline" />}>
Archive 12 alerts
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Archive 12 alerts?</AlertDialogTitle>
<AlertDialogDescription>
Archived alerts leave the queue and stop counting toward the SLA.
You can restore them from the archive for 90 days.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={pending}>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={archive} disabled={pending}>
{pending && <Spinner data-icon="inline-start" />}
{pending ? "Archiving" : "Archive alerts"}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}Accessibility
- The popup has
role="alertdialog". Focus moves into it on open, stays inside and returns to the trigger on close. Escape closes it; a click on the backdrop does not. AlertDialogTitlenames the dialog andAlertDialogDescriptiondescribes it. Render both: the description is where the consequence is spelled out.
API reference
Generated from the TypeScript source. className, style and children are accepted everywhere and omitted below.
AlertDialog
AlertDialog is a modal that stops the user to confirm a consequential action, such as deleting a record or discarding changes. Unlike Dialog, it does not close on an outside click. For a plain yes or no, call `confirm()` from the ConfirmDialog pattern instead of composing one; for a form or other content, use Dialog.
| Prop | Type | Default |
|---|---|---|
actionsRef | RefObject<DialogRootActions | null>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. | - |
defaultOpen | booleanWhether the dialog is initially open. | false |
defaultTriggerId | string | nullID 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>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: AlertDialogRootChangeEventDetails) => void)Event handler called when the alert dialog is opened or closed. | - |
onOpenChangeComplete | ((open: boolean) => void)Event handler called after any animations complete when the dialog is opened or closed. | - |
open | booleanWhether the dialog is currently open. | - |
triggerId | string | nullID 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
AlertDialogAction is the confirming button. It does not close the dialog: run the action, then close the dialog through its controlled `open` state, so a failed request can keep it open.
| Prop | Type | Default |
|---|---|---|
focusableWhenDisabled | booleanWhether the button should be focusable when disabled. | false |
nativeButton | booleanWhether 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>`). | true |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, ButtonState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
size | "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | nullHeight: `xs` 24px, `sm` 28px, `default` 32px, `lg` 36px. The `icon-*` sizes are square buttons of the same heights for a single icon. The icon follows the size name: 12px at `xs`, 14px at `sm`, 16px at `default` and `lg`. | - |
variant | "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | nullVisual weight. `default` is the filled primary action, one per view; `outline` and `secondary` are for the other actions; `ghost` has no fill until hover, for toolbars and icon buttons; `destructive` is for delete and other irreversible actions; `link` looks like inline text. | - |
AlertDialogCancel
AlertDialogCancel closes the dialog without acting. It renders an outline Button and takes its `variant` and `size`.
| Prop | Type | Default |
|---|---|---|
nativeButton | booleanWhether 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>`). | true |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogCloseState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
size | "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | nullHeight: `xs` 24px, `sm` 28px, `default` 32px, `lg` 36px. The `icon-*` sizes are square buttons of the same heights for a single icon. The icon follows the size name: 12px at `xs`, 14px at `sm`, 16px at `default` and `lg`. | default |
variant | "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | nullVisual weight. `default` is the filled primary action, one per view; `outline` and `secondary` are for the other actions; `ghost` has no fill until hover, for toolbars and icon buttons; `destructive` is for delete and other irreversible actions; `link` looks like inline text. | outline |
AlertDialogContent
AlertDialogContent is the dialog box, rendered in a portal over a dimmed backdrop. Like DialogContent, it is never taller than the viewport less a 16px margin on each side and scrolls when its content is longer.
| Prop | Type | Default |
|---|---|---|
finalFocus | boolean | RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null)Determines the element to focus when the dialog is closed. | - |
initialFocus | boolean | RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null)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. | - |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogPopupState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
size | "default" | "sm"`default` is 400px wide from the sm breakpoint, as wide as a `sm` Dialog, with the header aligned left. `sm` stays 320px wide, centers the header and lays the footer buttons out in two equal columns. | default |
AlertDialogDescription
| Prop | Type | Default |
|---|---|---|
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogDescriptionState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
AlertDialogFooter
No props of its own. Accepts the props of the element it renders.
AlertDialogHeader
No props of its own. Accepts the props of the element it renders.
AlertDialogMedia
AlertDialogMedia is the icon tile beside or above the title, for a symbol that sets the tone, such as a warning sign.
No props of its own. Accepts the props of the element it renders.
AlertDialogOverlay
| Prop | Type | Default |
|---|---|---|
forceRender | booleanWhether the backdrop is forced to render even when nested. | false |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogBackdropState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
AlertDialogPortal
| Prop | Type | Default |
|---|---|---|
container | HTMLElement | ShadowRoot | RefObject<HTMLElement | ShadowRoot | null> | nullA parent element to render the portal element into. | - |
keepMounted | booleanWhether to keep the portal mounted in the DOM while the popup is hidden. | false |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogPortalState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
AlertDialogTitle
| Prop | Type | Default |
|---|---|---|
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogTitleState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
AlertDialogTrigger
| Prop | Type | Default |
|---|---|---|
handle | AlertDialogHandle<unknown>A handle to associate the trigger with an alert dialog. Can be created with the AlertDialog.createHandle() method. | - |
id | stringID 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 | booleanWhether 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>`). | true |
payload | unknownA payload to pass to the dialog when it is opened. | - |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogTriggerState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |