Components
Alert Dialog
A modal dialog that interrupts the user with important content and expects an explicit confirm-or-cancel response before proceeding.
Basic usage
Wrap a trigger and content. Radix manages the open state, focus trap, and dismissal for you.
Destructive action
For irreversible actions, open from a destructive button and restyle AlertDialogAction by re-running buttonVariants. Do not hand-write the red: --destructive is the only red pinned across both palettes that carries a foreground colour.
ConfirmDialog composite
The package ships ConfirmDialog, a Tier 2 composite that wraps this component so every app asks the same way. It adds the icon slot, the destructive treatment, and an async onConfirm: while the promise is in flight the confirm button is disabled and the dialog refuses to close, so a slow API cannot be deleted twice. A throw keeps the dialog open and is handed to onError. The confirm, cancel and pending labels are all yours to set — the built-in "Đang xử lý…" is Vietnamese, so pass pendingLabel in an English app.
AlertDialog Props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Controlled open state of the dialog. Omit to let Radix manage it internally. |
defaultOpen | boolean | false | Open state when initially rendered, for uncontrolled usage. |
onOpenChange | (open: boolean) => void | — | Called when the open state changes (trigger, cancel, action, or Escape). |
childrenrequired | React.ReactNode | — | Trigger and content. There is no modal prop — an alert dialog is always modal. |
Parts
| Prop | Type | Default | Description |
|---|---|---|---|
AlertDialogTrigger | AlertDialogPrimitive.Trigger | — | The control that opens the dialog. Use asChild to render your own Button. |
AlertDialogPortal | AlertDialogPrimitive.Portal | — | Portal target. AlertDialogContent renders one already — reach for it only for a custom shell. |
AlertDialogOverlay | AlertDialogPrimitive.Overlay | — | Dimmed backdrop (bg-ink/60, dark:bg-ink/80, backdrop-blur). Also rendered for you by AlertDialogContent. |
AlertDialogContent | AlertDialogPrimitive.Content | — | The modal panel: portal + overlay + card surface. Fixed at sm:max-w-lg — unlike Dialog it takes no size prop and shows no corner close button. |
AlertDialogHeader | div | — | flex flex-col gap-2, centred on mobile and left-aligned from sm up. |
AlertDialogTitle | AlertDialogPrimitive.Title | — | Accessible name for the panel. Required — Radix warns when it is missing. |
AlertDialogDescription | AlertDialogPrimitive.Description | — | Supporting text linked to the panel via aria-describedby. |
AlertDialogFooter | div | — | Actions row: stacked and reversed on mobile, right-aligned from sm up. |
AlertDialogAction | AlertDialogPrimitive.Action | — | Confirming button. Pre-styled with buttonVariants() — the default gold button; closes the dialog on click. |
AlertDialogCancel | AlertDialogPrimitive.Cancel | — | Dismissing button. Pre-styled with buttonVariants({ variant: "outline" }); receives initial focus and closes the dialog. |
className | string | — | Additional Tailwind classes merged onto any part with cn(). |
ConfirmDialog Props
| Prop | Type | Default | Description |
|---|---|---|---|
titlerequired | ReactNode | — | Headline of the confirmation, rendered as AlertDialogTitle. |
onConfirmrequired | () => void | Promise<void> | — | Runs on confirm. Return a promise and the dialog stays open in a pending state until it settles — closing first and calling the API after is the reliable way to get a double delete. |
description | ReactNode | — | Supporting text under the title. |
children | ReactNode | — | Extra content between header and footer — the list of affected records, a reason field. |
trigger | ReactNode | — | Element that opens the dialog; wrapped in AlertDialogTrigger asChild. |
icon | ComponentType<{ className?: string }> | — | Optional leading icon, shown in a tinted square (destructive uses bg-destructive/15 with the -ink step). |
variant | 'default' | 'destructive' | 'default' | destructive paints the confirm button red — use it for delete and revoke. |
confirmLabel | string | 'Xác nhận' | Confirm button text. The package default is Vietnamese — pass English here. |
cancelLabel | string | 'Hủy' | Cancel button text. Vietnamese by default, same as above. |
pendingLabel | string | 'Đang xử lý…' | Confirm button text while an async onConfirm is in flight. Vietnamese by default — override it or the button flips language mid-action. |
confirmDisabled | boolean | false | Locks the confirm button, e.g. until the user has typed the record name. |
open | boolean | — | Controlled open state. Leave it out and the dialog manages its own, paired with trigger. |
onOpenChange | (open: boolean) => void | — | Called when the dialog opens or closes. Closing is ignored while onConfirm is pending. |
onError | (error: unknown) => void | — | Receives a throw from onConfirm so the app can raise a toast; without it the error is logged. The dialog stays open either way so the user can retry. |
className | string | — | Merged onto AlertDialogContent. |
Accessibility
- Built on Radix Alert Dialog: the content is rendered with
role="alertdialog"and focus is trapped inside while open. AlertDialogTitleandAlertDialogDescriptionare wired toaria-labelledbyandaria-describedbyautomatically — always include a Title.- Unlike a regular dialog, clicking the overlay does nothing: the user has to pick Cancel or the action. Escape still closes, which counts as cancelling.
- The Cancel button receives initial focus, and focus returns to the trigger when the dialog closes.
- There is no corner close button and no
sizeprop — the panel is alwayssm:max-w-lg. Reach forDialogwhen the user should be able to walk away.