Alert Dialog
The AlertDialog component is a specialized variant of Dialog designed for scenarios where users need to confirm or cancel potentially destructive or irreversible actions. It provides a consistent, accessible interface with built-in support for different themes and a loading state.
Each AlertDialog is opened by its own AlertDialog.Trigger — the button in each row below opens the dialog. The theme sets both the header icon and the confirm button’s color, and loading keeps the dialog open with a spinner while the confirm action runs.
Installation
import { AlertDialog, Button } from "@harnessio/ui/components";
// ...
return ( <AlertDialog.Root onConfirm={() => handleDelete()} theme="danger"> <AlertDialog.Trigger> <Button>Delete item</Button> </AlertDialog.Trigger>
<AlertDialog.Content title="Delete item"> Are you sure you want to delete this item? This action cannot be undone. <AlertDialog.Cancel>Cancel</AlertDialog.Cancel> <AlertDialog.Confirm>Delete</AlertDialog.Confirm> </AlertDialog.Content> </AlertDialog.Root>);The Trigger wraps whatever control should open the dialog — pass a Button (or any interactive element) as its single child. If you omit AlertDialog.Cancel and AlertDialog.Confirm, a default “Cancel” and “Confirm” pair is rendered for you.
Anatomy
All parts of the AlertDialog can be imported and composed as required:
<AlertDialog.Root onConfirm={() => {}}> <AlertDialog.Trigger> <Button>Open</Button> </AlertDialog.Trigger> <AlertDialog.Content title="Title"> {/* Message */} <AlertDialog.Cancel /> <AlertDialog.Confirm /> </AlertDialog.Content></AlertDialog.Root>- Root (
AlertDialog.Root) — wraps the whole thing and holds the shared state:theme,loading, and theonConfirm/onCancelhandlers. It can be left uncontrolled or driven withopen/onOpenChange. - Trigger — the control that opens the dialog. It renders its single child as the trigger (typically a Button) and registers it so focus returns there when the dialog closes.
- Content — the modal itself. It builds the header (title plus a theme icon), the body from whatever message you pass, and a footer holding the cancel and confirm buttons. It also renders a close ”✕” in the top corner.
- Cancel (optional) — the secondary button; it closes the dialog and calls
onCancel. If you don’t provide one, a default “Cancel” button is rendered. - Confirm (optional) — the primary button; it calls
onConfirm. If you don’t provide one, a default “Confirm” button is rendered.
The theme drives two things at once: the header icon (a warning triangle for warning, a circled ✕ for danger, none for default) and the confirm button’s color (red for danger, standard primary otherwise).
Confirming does not close the dialog on its own. Cancel, the ✕, Esc, and the backdrop all dismiss it, but AlertDialog.Confirm only calls onConfirm — you close it yourself once the action finishes. That’s what makes the loading state useful: keep the dialog open with a spinner while the work runs, then close it on success.
Best practices
Do
- Reserve
AlertDialogfor moments that genuinely need a decision — destructive, irreversible, or high-consequence actions — where stopping the user is worth the interruption. - Match the
themeto the stakes:dangerfor deleting or destroying,warningfor losing unsaved work,defaultfor a neutral confirmation. - Name the confirm button after the action (
Delete,Discard,Publish) rather than a generic “OK”, so the choice is clear without reading the body. - Use
loadingfor actions that take a moment, so the dialog stays put and the user sees progress instead of a frozen button.
Don’t
- Don’t use an
AlertDialogfor routine, low-risk confirmations or for messages that only need acknowledging — a toast or inline message interrupts less. - Don’t stack an
AlertDialogon top of another dialog; resolve one decision before asking for the next. - Don’t put form fields or long content inside it — reach for a full Dialog when the task is more than a yes/no decision.
Content
- Write the title as the question or the action — “Delete pipeline?”, “Discard draft?” — not a vague label like “Are you sure?”.
- Keep the body to a sentence or two: say what will happen and call out anything irreversible (“This action cannot be undone.”).
- Label the buttons with verbs that name the outcome; keep cancel as the safe, non-committal option.
Accessibility
AlertDialog is built on the Radix dialog primitive, so it comes with modal semantics out of the box: opening it traps focus, marks the rest of the page inert, and closing it returns focus to the trigger.
Keyboard
- The trigger is a normal control in the tab order and opens the dialog with Enter/Space.
- While open, Tab/Shift+Tab cycle only through the dialog’s controls; focus can’t leave the dialog.
- Esc closes the dialog (as does clicking the backdrop or the ✕), and focus returns to the element that opened it.
Labeling
- The dialog is a labeled modal (
role="dialog",aria-modal) whose accessible name comes from the requiredtitle. - The trigger exposes
aria-haspopup="dialog"andaria-expanded, so assistive tech announces that it opens a dialog and whether it’s currently open.
Focus & states
- On open, focus moves into the dialog; on close it’s restored to the trigger by the dialog’s focus manager.
- In the
loadingstate both footer buttons are disabled and the confirm button shows a spinner, so the pending action is conveyed visually and the controls can’t be re-triggered.
Known limitations
- The message isn’t wired up as the dialog’s description. Body content renders in the dialog body, but nothing is registered as its
aria-describedby— the header falls back to a hidden “No description available”. Screen readers reliably announce the title, but may not read the body as the description, so keep the essential meaning in thetitletoo. - The theme icon is decorative. The warning/danger icon in the header has no text alternative, so severity is conveyed only by color and icon. Don’t rely on the theme alone — state the consequence in the title or body text.
- Esc, the backdrop, and the ✕ don’t call
onCancel. Those dismissals fireonOpenChange(false)only;onCancelruns solely from the Cancel button. If you need cleanup on every dismissal, handle it inonOpenChangerather thanonCancel.
Properties
Root
Controls the dialog behavior and provides context for theme, loading, and action handlers.
Prop | Required | Default | Type |
|---|---|---|---|
| onConfirm | true | () => void | |
| onCancel | false | () => void | |
| theme | false | 'default' | 'default' | 'warning' | 'danger' |
| open | false | boolean | |
| onOpenChange | false | (open: boolean) => void | |
| loading | false | false | boolean |
| children | true | ReactNode |
Content
The main container for the alert dialog. Renders the header (title + theme icon), the body, a footer with the cancel/confirm buttons, and a close ✕.
Prop | Required | Default | Type |
|---|---|---|---|
| title | true | string | |
| children | false | ReactNode |
Trigger
Wraps the control that opens the dialog. Renders its single child as the trigger and registers it so focus returns there on close.
Prop | Required | Default | Type |
|---|---|---|---|
| children | true | ReactNode |
Cancel
Optional cancel button. Closes the dialog and calls onCancel. If not provided, a default “Cancel” button is rendered.
Prop | Required | Default | Type |
|---|---|---|---|
| children | false | ReactNode |
Confirm
Optional confirm button. Calls onConfirm (and shows a spinner while loading). If not provided, a default “Confirm” button is rendered.
Prop | Required | Default | Type |
|---|---|---|---|
| children | false | ReactNode |