Alert Dialog
Alert Dialog: anatomy, keyboard behavior, state, and theming in Askr.
Example
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, AlertDialogTrigger, Button } from '@askrjs/themes/components';
<AlertDialog>
<AlertDialogTrigger asChild><Button variant="destructive">Delete project</Button></AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogTitle>Delete project?</AlertDialogTitle>
<AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Delete</AlertDialogAction>
</AlertDialogContent>
</AlertDialog>Published props
Generated from the TypeScript declarations shipped by the installed package. Named types in the Type column define the accepted values.
AlertDialogActionAsChildProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild: true; | — | — |
children | children: JSXElement; | — | — |
disabled | disabled?: boolean | undefined; | — | — |
onPress | onPress?: ((event: PressEvent) => void) | undefined; | — | — |
ref | ref?: Ref<Element>; | — | — |
type | type?: undefined; | — | — |
AlertDialogActionProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild?: false | undefined; | — | — |
children | children?: unknown; | — | — |
disabled | disabled?: boolean | undefined; | — | — |
onPress | onPress?: ((event: PressEvent) => void) | undefined; | — | — |
ref | ref?: Ref<HTMLButtonElement>; | — | — |
type | type?: "button" | "submit" | "reset" | undefined; | — | — |
AlertDialogCancelAsChildProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild: true; | — | — |
children | children: JSXElement; | — | — |
disabled | disabled?: boolean | undefined; | — | — |
onPress | onPress?: ((event: PressEvent) => void) | undefined; | — | — |
ref | ref?: Ref<Element>; | — | — |
type | type?: undefined; | — | — |
AlertDialogCancelProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild?: false | undefined; | — | — |
children | children?: unknown; | — | — |
disabled | disabled?: boolean | undefined; | — | — |
onPress | onPress?: ((event: PressEvent) => void) | undefined; | — | — |
ref | ref?: Ref<HTMLButtonElement>; | — | — |
type | type?: "button" | "submit" | "reset" | undefined; | — | — |
AlertDialogContentAsChildProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild: true; | — | — |
children | children: JSXElement; | — | — |
forceMount | forceMount?: boolean | undefined; | — | — |
onDismiss | onDismiss?: (() => void) | undefined; | — | — |
onEscapeKeyDown | onEscapeKeyDown?: ((event: KeyboardEvent) => void) | undefined; | — | — |
onInteractOutside | onInteractOutside?: ((event: Event) => void) | undefined; | — | — |
onPointerDownOutside | onPointerDownOutside?: ((event: PointerEvent) => void) | undefined; | — | — |
ref | ref?: Ref<Element>; | — | — |
role | role?: "alertdialog" | "dialog" | undefined; | — | — |
AlertDialogContentOwnProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
forceMount | forceMount?: boolean | undefined; | — | — |
onDismiss | onDismiss?: (() => void) | undefined; | — | — |
onEscapeKeyDown | onEscapeKeyDown?: ((event: KeyboardEvent) => void) | undefined; | — | — |
onInteractOutside | onInteractOutside?: ((event: Event) => void) | undefined; | — | — |
onPointerDownOutside | onPointerDownOutside?: ((event: PointerEvent) => void) | undefined; | — | — |
role | role?: "alertdialog" | "dialog" | undefined; | — | — |
AlertDialogContentProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild?: false | undefined; | — | — |
children | children?: unknown; | — | — |
forceMount | forceMount?: boolean | undefined; | — | — |
onDismiss | onDismiss?: (() => void) | undefined; | — | — |
onEscapeKeyDown | onEscapeKeyDown?: ((event: KeyboardEvent) => void) | undefined; | — | — |
onInteractOutside | onInteractOutside?: ((event: Event) => void) | undefined; | — | — |
onPointerDownOutside | onPointerDownOutside?: ((event: PointerEvent) => void) | undefined; | — | — |
ref | ref?: Ref<HTMLDivElement>; | — | — |
role | role?: "alertdialog" | "dialog" | undefined; | — | — |
AlertDialogDescriptionAsChildProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild: true; | — | — |
children | children: JSXElement; | — | — |
ref | ref?: Ref<Element>; | — | — |
AlertDialogDescriptionProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild?: false | undefined; | — | — |
children | children?: unknown; | — | — |
ref | ref?: Ref<HTMLParagraphElement>; | — | — |
AlertDialogOverlayAsChildProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild: true; | — | — |
children | children: JSXElement; | — | — |
forceMount | forceMount?: boolean | undefined; | — | — |
ref | ref?: Ref<Element>; | — | — |
AlertDialogOverlayOwnProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
forceMount | forceMount?: boolean | undefined; | — | — |
AlertDialogOverlayProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild?: false | undefined; | — | — |
children | children?: unknown; | — | — |
forceMount | forceMount?: boolean | undefined; | — | — |
ref | ref?: Ref<HTMLDivElement>; | — | — |
AlertDialogOwnProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
children | children?: unknown; | — | — |
defaultOpen | defaultOpen?: boolean | undefined; | — | — |
id | id?: string | undefined; | — | — |
modal | modal?: boolean | undefined; | — | — |
onOpenChange | onOpenChange?: ((open: boolean) => void) | undefined; | — | — |
open | open?: boolean | undefined; | — | — |
AlertDialogPortalProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
children | children?: unknown; | — | — |
AlertDialogProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
children | children?: unknown; | — | — |
defaultOpen | defaultOpen?: boolean | undefined; | — | — |
id | id?: string | undefined; | — | — |
modal | modal?: boolean | undefined; | — | — |
onOpenChange | onOpenChange?: ((open: boolean) => void) | undefined; | — | — |
open | open?: boolean | undefined; | — | — |
AlertDialogTitleAsChildProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild: true; | — | — |
children | children: JSXElement; | — | — |
ref | ref?: Ref<Element>; | — | — |
AlertDialogTitleProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild?: false | undefined; | — | — |
children | children?: unknown; | — | — |
ref | ref?: Ref<HTMLHeadingElement>; | — | — |
AlertDialogTriggerAsChildProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild: true; | — | — |
children | children: JSXElement; | — | — |
disabled | disabled?: boolean | undefined; | — | — |
onPress | onPress?: ((event: PressEvent) => void) | undefined; | — | — |
ref | ref?: Ref<Element>; | — | — |
type | type?: undefined; | — | — |
AlertDialogTriggerProps
Import from @askrjs/ui/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild?: false | undefined; | — | — |
children | children?: unknown; | — | — |
disabled | disabled?: boolean | undefined; | — | — |
onPress | onPress?: ((event: PressEvent) => void) | undefined; | — | — |
ref | ref?: Ref<HTMLButtonElement>; | — | — |
type | type?: "button" | "submit" | "reset" | undefined; | — | — |
Purpose
Alert Dialog is Dialog configured for one job: forcing a decision. It's for destructive or hard-to-reverse actions — deleting a record, discarding unsaved changes — where you want to interrupt the user and make them explicitly confirm or back out rather than dismissing it accidentally with a stray click or Escape.
Install and import
Import behavior from `@askrjs/ui/alert-dialog` and styling from `@askrjs/themes/alert-dialog`, both real subpaths. `AlertDialogAction` and `AlertDialogCancel` really are `DialogClose` under alias names with no logic of their own, and `AlertDialogOverlay`/`AlertDialogTitle`/`AlertDialogDescription` pass straight through to Dialog's parts — but `AlertDialogTrigger` and `AlertDialogContent` aren't bare re-exports; both wrap the underlying Dialog part with alert-dialog-specific dismissal guarding, so don't assume every piece in this tree is a zero-logic alias just because most of them are.
Live examples
Structure mirrors Dialog: `AlertDialog` wraps an `AlertDialogTrigger` and a portal containing `AlertDialogContent`, `AlertDialogTitle`, and `AlertDialogDescription`. The part that's actually distinct is the footer — pair `AlertDialogCancel` with `AlertDialogAction` so there's always an explicit way to back out alongside the destructive action.
Anatomy
`AlertDialogContent` is a dedicated component, but it does not set `role="alertdialog"` for you — it renders through `DialogContent` with the same default role Dialog uses, so pass `role="alertdialog"` explicitly if you want that stronger semantic than plain `role="dialog"`. What `AlertDialogContent` and `AlertDialogTrigger` actually add over a straight Dialog re-export is behavioral, not role-related: the trigger guards against re-activation dismissing an already-open dialog, and the content wraps `onEscapeKeyDown`/`onPointerDownOutside` so outside pointer dismissal is blocked while Escape still works — reflecting the "confirm or explicitly cancel" pattern this component is for.
State model
State is the same open/closed boolean as Dialog, since `AlertDialogProps` is literally `DialogProps` under a type alias — `open`, `defaultOpen`, and `onOpenChange` behave identically. The meaningful difference isn't in the state shape, it's in what the two footer actions are expected to do: `AlertDialogCancel` should always close without side effects, `AlertDialogAction` should perform the action and then close.
Keyboard and accessibility
The a11y contract calls for both a primary action and a cancel action to be present — `ALERT_DIALOG_A11Y_CONTRACT.ACTION_REQUIREMENTS` flags both as required, so don't ship an alert dialog with only a single button. `aria-labelledby`/`aria-describedby` from `DialogTitle`/`DialogDescription` are wired up the same way Dialog does it, but `role="alertdialog"` is not applied automatically — the component defaults to Dialog's own role, so set `role="alertdialog"` on `AlertDialogContent` explicitly if you want assistive tech to treat it as an interruption needing an explicit response rather than a plain dialog.
Styling and tokens
Because the overlay, content surface, and animations are shared with Dialog, alert dialogs pick up the same `--ak-color-backdrop`, `--ak-color-surface-raised`, `--ak-color-border`, and z-index tokens out of the box. The theme layer's own `alert-dialog.css` is intentionally small — it only strengthens the content's border (`border-width: 1px` against `--ak-color-border`) on top of Dialog's shared styling; it doesn't add its own action-row layout or automatically apply a destructive variant to `AlertDialogAction` — pick that button variant yourself the same way you would on any other `Button`.
API
`AlertDialogAction` and `AlertDialogCancel` are both just `DialogClose` — button-like components with `onPress`/`disabled`, no special props of their own beyond what Dialog's close button already supports. `AlertDialogContent` supports the same `forceMount`, `onEscapeKeyDown`, `onPointerDownOutside`, `onInteractOutside`, and `onDismiss` props as `DialogContent`; there's no alert-dialog-specific prop surface beyond the role.
Edge cases
Because `AlertDialogCancel` and `AlertDialogAction` are both plain close buttons under the hood, nothing stops you from wiring `onPress` on `AlertDialogCancel` to do something other than a no-op cancel — resist that, since it breaks the pattern users expect from this component. If the destructive action can fail, keep the dialog open and surface the error inside `AlertDialogContent` rather than letting `AlertDialogAction` close it optimistically.
Related pages
See Dialog for the full prop reference and the shared portal/overlay/content anatomy that Alert Dialog builds on. If the interruption doesn't need a confirm/cancel pair — just information the user can dismiss with a click anywhere — a plain Dialog or a Popover is a better fit.