Askr documentation
UI & Components

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.

PropTypeDefaultDescription
asChildasChild: true;
childrenchildren: JSXElement;
disableddisabled?: boolean | undefined;
onPressonPress?: ((event: PressEvent) => void) | undefined;
refref?: Ref<Element>;
typetype?: undefined;

AlertDialogActionProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;
disableddisabled?: boolean | undefined;
onPressonPress?: ((event: PressEvent) => void) | undefined;
refref?: Ref<HTMLButtonElement>;
typetype?: "button" | "submit" | "reset" | undefined;

AlertDialogCancelAsChildProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
asChildasChild: true;
childrenchildren: JSXElement;
disableddisabled?: boolean | undefined;
onPressonPress?: ((event: PressEvent) => void) | undefined;
refref?: Ref<Element>;
typetype?: undefined;

AlertDialogCancelProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;
disableddisabled?: boolean | undefined;
onPressonPress?: ((event: PressEvent) => void) | undefined;
refref?: Ref<HTMLButtonElement>;
typetype?: "button" | "submit" | "reset" | undefined;

AlertDialogContentAsChildProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
asChildasChild: true;
childrenchildren: JSXElement;
forceMountforceMount?: boolean | undefined;
onDismissonDismiss?: (() => void) | undefined;
onEscapeKeyDownonEscapeKeyDown?: ((event: KeyboardEvent) => void) | undefined;
onInteractOutsideonInteractOutside?: ((event: Event) => void) | undefined;
onPointerDownOutsideonPointerDownOutside?: ((event: PointerEvent) => void) | undefined;
refref?: Ref<Element>;
rolerole?: "alertdialog" | "dialog" | undefined;

AlertDialogContentOwnProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
forceMountforceMount?: boolean | undefined;
onDismissonDismiss?: (() => void) | undefined;
onEscapeKeyDownonEscapeKeyDown?: ((event: KeyboardEvent) => void) | undefined;
onInteractOutsideonInteractOutside?: ((event: Event) => void) | undefined;
onPointerDownOutsideonPointerDownOutside?: ((event: PointerEvent) => void) | undefined;
rolerole?: "alertdialog" | "dialog" | undefined;

AlertDialogContentProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;
forceMountforceMount?: boolean | undefined;
onDismissonDismiss?: (() => void) | undefined;
onEscapeKeyDownonEscapeKeyDown?: ((event: KeyboardEvent) => void) | undefined;
onInteractOutsideonInteractOutside?: ((event: Event) => void) | undefined;
onPointerDownOutsideonPointerDownOutside?: ((event: PointerEvent) => void) | undefined;
refref?: Ref<HTMLDivElement>;
rolerole?: "alertdialog" | "dialog" | undefined;

AlertDialogDescriptionAsChildProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
asChildasChild: true;
childrenchildren: JSXElement;
refref?: Ref<Element>;

AlertDialogDescriptionProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;
refref?: Ref<HTMLParagraphElement>;

AlertDialogOverlayAsChildProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
asChildasChild: true;
childrenchildren: JSXElement;
forceMountforceMount?: boolean | undefined;
refref?: Ref<Element>;

AlertDialogOverlayOwnProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
forceMountforceMount?: boolean | undefined;

AlertDialogOverlayProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;
forceMountforceMount?: boolean | undefined;
refref?: Ref<HTMLDivElement>;

AlertDialogOwnProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
childrenchildren?: unknown;
defaultOpendefaultOpen?: boolean | undefined;
idid?: string | undefined;
modalmodal?: boolean | undefined;
onOpenChangeonOpenChange?: ((open: boolean) => void) | undefined;
openopen?: boolean | undefined;

AlertDialogPortalProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
childrenchildren?: unknown;

AlertDialogProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
childrenchildren?: unknown;
defaultOpendefaultOpen?: boolean | undefined;
idid?: string | undefined;
modalmodal?: boolean | undefined;
onOpenChangeonOpenChange?: ((open: boolean) => void) | undefined;
openopen?: boolean | undefined;

AlertDialogTitleAsChildProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
asChildasChild: true;
childrenchildren: JSXElement;
refref?: Ref<Element>;

AlertDialogTitleProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;
refref?: Ref<HTMLHeadingElement>;

AlertDialogTriggerAsChildProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
asChildasChild: true;
childrenchildren: JSXElement;
disableddisabled?: boolean | undefined;
onPressonPress?: ((event: PressEvent) => void) | undefined;
refref?: Ref<Element>;
typetype?: undefined;

AlertDialogTriggerProps

Import from @askrjs/ui/alert-dialog.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;
disableddisabled?: boolean | undefined;
onPressonPress?: ((event: PressEvent) => void) | undefined;
refref?: Ref<HTMLButtonElement>;
typetype?: "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.

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.