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/themes/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/themes/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/themes/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/themes/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/themes/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>; | — | — |
restoreFocus | restoreFocus?: HTMLElement | (() => HTMLElement | null) | null | undefined; | — | Explicit focus target used when a controlled dialog has no persistent trigger. |
role | role?: "dialog" | "alertdialog" | undefined; | — | — |
AlertDialogContentOwnProps
Import from @askrjs/themes/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; | — | — |
restoreFocus | restoreFocus?: HTMLElement | (() => HTMLElement | null) | null | undefined; | — | Explicit focus target used when a controlled dialog has no persistent trigger. |
role | role?: "dialog" | "alertdialog" | undefined; | — | — |
AlertDialogContentProps
Import from @askrjs/themes/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>; | — | — |
restoreFocus | restoreFocus?: HTMLElement | (() => HTMLElement | null) | null | undefined; | — | Explicit focus target used when a controlled dialog has no persistent trigger. |
role | role?: "dialog" | "alertdialog" | undefined; | — | — |
AlertDialogDescriptionAsChildProps
Import from @askrjs/themes/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild: true; | — | — |
children | children: JSXElement; | — | — |
ref | ref?: Ref<Element>; | — | — |
AlertDialogDescriptionProps
Import from @askrjs/themes/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild?: false | undefined; | — | — |
children | children?: unknown; | — | — |
ref | ref?: Ref<HTMLParagraphElement>; | — | — |
AlertDialogOverlayAsChildProps
Import from @askrjs/themes/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild: true; | — | — |
children | children: JSXElement; | — | — |
forceMount | forceMount?: boolean | undefined; | — | — |
ref | ref?: Ref<Element>; | — | — |
AlertDialogOverlayOwnProps
Import from @askrjs/themes/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
forceMount | forceMount?: boolean | undefined; | — | — |
AlertDialogOverlayProps
Import from @askrjs/themes/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/themes/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/themes/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
children | children?: unknown; | — | — |
AlertDialogProps
Import from @askrjs/themes/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/themes/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild: true; | — | — |
children | children: JSXElement; | — | — |
ref | ref?: Ref<Element>; | — | — |
AlertDialogTitleProps
Import from @askrjs/themes/alert-dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild?: false | undefined; | — | — |
children | children?: unknown; | — | — |
ref | ref?: Ref<HTMLHeadingElement>; | — | — |
AlertDialogTriggerAsChildProps
Import from @askrjs/themes/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/themes/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
Provide both a primary action and a cancel action. DialogTitle and DialogDescription supply accessible naming and description. Set role="alertdialog" on AlertDialogContent when the interaction requires an interrupting confirmation, and test focus placement, keyboard activation and cancellation in the composed tree.
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.