AlertDialog for deletes

Stray clicks can’t close it, and focus opens on Cancel.

From shadcn (Vercel plugin): Destructive flows and Anti-patterns.

One delete button, two dialogs

Open each, then click outside, press Escape, or press Enter.

DialogBad

acme-web

Active

3 deployments · 2 domains

<Dialog>deleted 0 · no answer 0
AlertDialogGood

acme-web

Active

3 deployments · 2 domains

<AlertDialog>deleted 0 · no answer 0
<AlertDialog>
  <AlertDialogTrigger asChild>
    <Button variant="destructive">Delete project</Button>
  </AlertDialogTrigger>
  <AlertDialogContent>
    <AlertDialogHeader>
      <AlertDialogTitle>Delete “acme-web”?</AlertDialogTitle>
      <AlertDialogDescription>
        Its 3 deployments and 2 domains go with it. This can’t be undone.
      </AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>Cancel</AlertDialogCancel>
      <AlertDialogAction onClick={deleteProject}>Delete project</AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>

The hasty keyboard run

Press Run, then move AlertDialog focus to Delete.

DialogBad
Projects, Dialog confirmation
ProjectStatusActions
<Dialog>deleted 0 of 4
AlertDialogGood
Projects, AlertDialog confirmation
ProjectStatusActions
focus: AlertDialogCanceldeleted 0 of 4
const [target, setTarget] = useState<Project | null>(null)

<DropdownMenuItem variant="destructive" onSelect={() => setTarget(project)}>
  Delete
</DropdownMenuItem>

<AlertDialog open={target !== null} onOpenChange={(open) => !open && setTarget(null)}>
  <AlertDialogContent>
    <AlertDialogHeader>…</AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>Cancel</AlertDialogCancel> {/* focused on open */}
      <AlertDialogAction onClick={() => remove(target.id)}>Delete</AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>

Why it matters, pros and cons

Why it matters

Dialog closes on a stray click; Enter hits whatever has focus. AlertDialog waits.

Pros

  • A double Enter lands on Cancel.
  • A stray click can’t dismiss it.
  • Screen readers announce alertdialog.

Cons

  • Reversible actions want an undo toast, not a modal.
  • Confirming everything teaches people to click through.
  • Costly deletes also need type-the-name confirmation.