Skip to content

Alert Dialog

Confirmation dialog for destructive or irreversible actions. It cannot be dismissed by clicking outside, and can ask the user to type a word before the confirm button works. Open it with DialogTrigger.

Overlayv0.1.0experimentalWCAG 2.2 AAView spec
import { AlertDialog, Button, DialogTrigger } from "@rdloom/react";

export default function AlertDialogBasicExample() {
  return (
    <DialogTrigger>
      <Button variant="secondary">Discard draft</Button>
      <AlertDialog
        title="Discard this draft?"
        description="Your changes have not been saved and will be lost."
        confirmLabel="Discard draft"
        cancelLabel="Keep editing"
      />
    </DialogTrigger>
  );
}

Installation

npx rdloom add alert-dialog

Copies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.

Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/alert-dialog.json

Works, but without upgrade tracking.

Usage

import { AlertDialog, Button, DialogTrigger } from "@rdloom/react";

<DialogTrigger>
  <Button variant="secondary">Discard draft</Button>
  <AlertDialog
    title="Discard this draft?"
    description="Your changes have not been saved and will be lost."
    confirmLabel="Discard draft"
    cancelLabel="Keep editing"
  />
</DialogTrigger>

Danger delete

import { AlertDialog, Button, DialogTrigger } from "@rdloom/react";

export default function AlertDialogDangerDeleteExample() {
  return (
    <DialogTrigger>
      <Button variant="danger">Delete user</Button>
      <AlertDialog
        tone="danger"
        title="Delete this user?"
        description="Asha Menon will lose access immediately. This cannot be undone."
        confirmLabel="Delete user"
        onConfirm={() => {
          // Remove the user here.
        }}
      />
    </DialogTrigger>
  );
}

Type to confirm

import { AlertDialog, Button, DialogTrigger } from "@rdloom/react";

export default function AlertDialogTypeToConfirmExample() {
  return (
    <DialogTrigger>
      <Button variant="danger">Delete workspace</Button>
      <AlertDialog
        tone="danger"
        title="Delete the Northwind workspace?"
        description="All projects, invoices and members in this workspace are removed for good."
        confirmLabel="Delete workspace"
        confirmText="Northwind"
        onConfirm={() => {
          // Remove the workspace here.
        }}
      />
    </DialogTrigger>
  );
}

Async confirm

import { AlertDialog, Button, DialogTrigger } from "@rdloom/react";

export default function AlertDialogAsyncConfirmExample() {
  return (
    <DialogTrigger>
      <Button variant="danger">Archive invoices</Button>
      <AlertDialog
        tone="danger"
        title="Archive 24 invoices?"
        description="They move out of the active list. The dialog stays open until the request finishes."
        confirmLabel="Archive invoices"
        onConfirm={() => new Promise<void>((resolve) => setTimeout(resolve, 1500))}
      />
    </DialogTrigger>
  );
}

API Reference

Defined by the spec. Components also accept the props of the React Aria component they wrap.

PropTypeDefault
titlerequired

Heading, also the dialog's accessible name. Phrase it as the question being asked, such as Delete this user?

stringnone
description

What will happen and whether it can be undone. Wired to the dialog with aria-describedby.

stringnone
confirmLabel

Label of the confirm button. Name the action, such as Delete user.

string"Confirm"
cancelLabel

Label of the cancel button.

string"Cancel"
tone

Danger styles the confirm button as destructive and starts focus on the cancel button.

"default" | "danger""default"
confirmText

A word the user must type exactly (such as the resource name) before the confirm button is enabled.

stringnone
onConfirm

Called when the user confirms. If it returns a promise, the confirm button shows a pending state and the dialog cannot be dismissed until it settles. The dialog closes when it resolves and stays open if it rejects.

() => void | Promise<void>none
onCancel

Called when the user cancels with the cancel button or Esc.

() => voidnone
isOpen

Controlled open state, for dialogs opened without a DialogTrigger.

booleannone
defaultOpen

Initial open state when uncontrolled and used without a DialogTrigger.

booleannone
onOpenChange

Called when the dialog opens or closes (without a DialogTrigger).

(isOpen: boolean) => voidnone

Accessibility

Role alertdialog, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.

Keyboard

  • Esc cancels
  • Tab and Shift+Tab stay inside the dialog
  • Enter in the confirmation field confirms once the word matches

Screen readers announce

  • Opening announces an alert dialog with its title, then reads the description
  • In the danger tone focus lands on Cancel, so a stray Enter does not destroy anything
  • With confirmText, the field is labelled with the word to type, and Confirm is announced as dimmed until it matches
  • While pending the confirm button is announced as busy
  • Closing announces the button that opened it (focus is back there)

What your code must do

  • Focus moves into the dialog on open (to Cancel for the danger tone) and returns to the trigger on close
  • Clicking outside does not dismiss it
  • The description is linked with aria-describedby so it is read on open
  • While a confirm is pending, Esc and Cancel are blocked and the confirm button is marked busy
  • Content behind the dialog is hidden from assistive technology

Guidelines

Use it when

  • Confirming a destructive or irreversible action such as deleting a record
  • Making the user prove intent by typing a resource name

Avoid it when

  • Non-destructive choices: use Dialog
  • Actions that can be undone: use a toast with Undo
  • Forms with several fields: use Dialog or Sheet

Don't

  • Vague buttons like OK or Yes: name the action
  • Using it for routine confirmations, which trains people to click through
  • Closing the dialog yourself before an async onConfirm settles

Design tokens

The semantic tokens this component uses. Change them once and every component follows; see Design tokens.

  • --rd-color-overlay-backdrop
  • --rd-color-surface-raised
  • --rd-color-border-default
  • --rd-color-text-default
  • --rd-color-text-muted
  • --rd-radius-overlay