Capítulo 12 de 51
A modal dialog that interrupts the user and requires an explicit response (confirm/cancel) — same overlay mechanics as Dialog, plus a Backdrop, scrollable Viewport, and an imperative Handle/createHandle() system for wiring triggers that live outside the dialog's own tree (e.g. inside a Menu).
Root → Trigger, Portal → Backdrop, Viewport → Popup → Title, Description, Close.Handle: AlertDialog.createHandle() returns a Handle<Payload> passed to both Root and any Triggers rendered outside it — lets a Menu item or other unrelated component open the dialog without controlling its state directly. Handle exposes isOpen (readonly), open(triggerId), openWithPayload(payload), close() — all callable only from event handlers/effects, never during render, and only while a Root using that handle is mounted.actionsRef: alternative imperative control scoped to one Root — exposes unmount() (call after an externally-controlled closing animation finishes) and close().initialFocus/finalFocus on Popup: boolean | RefObject | (interactionType) => element|boolean|null. Default: focus moves to first tabbable element, except touch-opened dialogs focus the popup itself (avoids triggering the virtual keyboard). finalFocus default: returns to trigger or previously-focused element.Popup/Viewport expose data-nested and data-nested-dialog-open; a --nested-dialogs CSS variable on Popup counts nesting depth. Child dialogs' backdrops don't render, so the parent dialog stays visible cleanly behind the top one.payload: Trigger accepts a payload prop passed through to the dialog when opened via that trigger — useful for "which item triggered this confirm dialog" data without external state./* Detached trigger (inside a Menu) controlling an AlertDialog via a Handle */
const dialogHandle = AlertDialog.createHandle();
<Menu.Item render={<AlertDialog.Trigger handle={dialogHandle} />}>Delete</Menu.Item>
<AlertDialog.Root handle={dialogHandle}>
<AlertDialog.Portal>
<AlertDialog.Backdrop />
<AlertDialog.Popup>
<AlertDialog.Title>Delete item?</AlertDialog.Title>
<AlertDialog.Close>Cancel</AlertDialog.Close>
</AlertDialog.Popup>
</AlertDialog.Portal>
</AlertDialog.Root>
handle is the idiomatic way to connect a trigger to a dialog when they can't be siblings in the tree (e.g. trigger lives inside a Menu.Popup, dialog needs to render outside it) — avoids manually lifting open/onOpenChange state.| Part | Notable props | Notable data attributes |
|---|---|---|
Root | open/onOpenChange, defaultOpen, handle, actionsRef, triggerId/defaultTriggerId, onOpenChangeComplete | — |
Trigger | handle, payload, id, nativeButton | data-popup-open, data-disabled |
Backdrop | forceRender | data-open, data-closed, data-starting-style, data-ending-style |
Popup | initialFocus, finalFocus | data-open, data-closed, data-nested, data-nested-dialog-open, data-starting-style, data-ending-style (CSS var --nested-dialogs) |
Viewport | — | same open/nested/animation attributes as Popup |
Close | nativeButton | data-disabled |
AlertDialog.createHandle() + handle prop whenever the trigger and the dialog can't be siblings — e.g. a delete confirmation triggered from inside a Menu.Item — instead of lifting open/close state manually.Backdrop's forceRender only when a nested-dialog backdrop is being unexpectedly suppressed and you need it visible anyway; the default (no backdrop on nested dialogs) is deliberate for a clean stacked look.Handle's imperative methods (open, close, openWithPayload) must be called from event handlers or effects — calling them during render is unsupported and silently ignored before a Root mounts.ChangeEventReason values (trigger-press, outside-press, escape-key, close-press, focus-out, imperative-action) usable with eventDetails.cancel()/allowPropagation().