Alert Dialog
Asks before something that cannot be undone; a click outside never closes it.
Installation
npx @yolkui/cli add alert-dialogUsage
An Alert Dialog asks a question that must be answered before something that cannot be undone. For anything else, a form, a setting, a long text, use Dialog. It is Dialog's surface without the ×: the choice is a button. AlertDialogCancel goes first and AlertDialogAction last; the action names what happens ("Delete project", not "OK") and takes variant="destructive" when it destroys something. Both close the alert (the action after its work, when its onClick is async); a click on the backdrop does not, Escape does, as Cancel.
- Ask only when it matters: an alert for every small action teaches people to click through without reading.
Layout
layout decides the text and the buttons, and nothing else. "start", the default above, puts the text at the start and the buttons at the end, stacked on a phone with the action on top, as Dialog: for a question with more to read. "center" centres the text in balanced lines and gives the buttons equal shares of the row at every width, as macOS and iOS alerts: for a short, clear question, usually with an icon.
Size
size decides the width, and nothing else: 448px at the start, 384px centred, 320px at "sm". As shadcn's sm, size="sm" centres unless you pass a layout. For a short yes or no.
Icon
AlertDialogMedia, first in the header, holds an icon in a 40px square, with a 16px glyph as every icon here. It looks best centred, above the title; at the start it sits beside the title and description. tone paints it as the subtle Badge of that tone; match the action, tone="destructive" for a delete. It is hidden from screen readers, so the title must say everything.
From a menu
"Delete…" in a Dropdown Menu or Context Menu opens the alert: control open and set it in the item's onClick. The menu closes first, and when the alert closes focus goes back to the menu's button. End the item's label with an ellipsis: it asks before it acts.
Work that takes time
When the action waits for a server, give AlertDialogAction an async onClick (or return the promise). The alert stays open while it runs: the action shows its loading state, Escape is off, and it closes when the work is done, so a delete is never left running with nothing on screen to say so. If the promise rejects or resolves to false the alert stays open, ready to show the error and try again (show it in the alert with role="alert", so a screen reader hears it). Cancel still works while it loads, so a request that never answers locks no one in; abort the request in onOpenChange if it should stop. loadingText ("Working" by default) is announced to screen readers.
- When your code tracks the work itself and has no promise to return (mutate() and isPending in a data library, a transition), pass loading={isPending}. The action then loads while it is true and never closes the alert by itself; close it from your code when the work succeeds (setOpen(false) in onSuccess).
- Code pasted from shadcn's Base UI alert, where the action never closes, works when its onClick is async and closes the alert itself.
API
Our props on top of Base UI's AlertDialog (open, defaultOpen, onOpenChange, actionsRef, handle and the rest work as in Base UI). A click outside never closes an alert.
| Prop | On | Values | Default |
|---|---|---|---|
layout | AlertDialogContent | "start" · "center" | "start", "center" at size="sm" |
size | AlertDialogContent | "default" · "sm" | "default" (448, centred 384; sm 320) |
tone | AlertDialogMedia | neutral · brand · accent · success · info · warning · destructive | "neutral" |
variant | AlertDialogAction | Button's variants | "default" |
onClick | AlertDialogAction | a function; when it returns a promise the alert waits for it, and stays open on a rejection or false | — |
loading | AlertDialogAction | true · false: your own work state; the action loads and your code closes the alert | — |
loadingText | AlertDialogAction | text announced when the work starts | "Working" |
variant | AlertDialogCancel | Button's variants | "outline" |
createAlertDialogHandle() | — | links triggers anywhere to one alert, with a payload | — |
Accessibility
The popup is an alertdialog, named by its title and described by its description, which screen readers read as it opens. Focus starts on Cancel, wherever it sits in the footer, so Enter on a freshly opened alert never deletes anything; Tab stays inside, and focus returns to the trigger when it closes.