elements
Demos UI Feed Discuss Install
Style System
  • Style
  • Themes
  • Behaviors
Forms
  • Button
  • ButtonGroup
  • Checkbox
  • ColorPicker
  • Combobox
  • Field
  • FileUpload
  • Input
  • InputGroup
  • InputOTP
  • Label
  • NativeSelect
  • NumberInput
  • PasswordInput
  • RadioGroup
  • Select
  • Slider
  • Switch
  • TagsInput
  • Textarea
  • Toggle
  • ToggleGroup
Overlays
  • AlertDialog
  • Command
  • ContextMenu
  • Dialog
  • Drawer
  • DropdownMenu
  • HoverCard
  • Menubar
  • NavigationMenu
  • Popover
  • Sheet
  • Toast
  • Tooltip
Display
  • Accordion
  • Alert
  • AspectRatio
  • Avatar
  • Badge
  • Breadcrumb
  • Card
  • Collapsible
  • Empty
  • Item
  • Kbd
  • Pagination
  • Progress
  • Separator
  • Skeleton
  • Spinner
  • Table
  • Tabs
  • Tree
Layout
  • Carousel
  • Resizable
  • ScrollArea
  • Sidebar
Dates
  • Calendar
  • DatePicker
  • DateRangePicker
  • TimePicker
Data
  • AreaChart
  • BarChart
  • Chart
  • DataTable
  • Kanban
  • LineChart
  • PieChart
  • RadarChart
  • RadialChart
  • VirtualList
Chat
  • Attachment
  • Message
  • MessageList
  • PromptInput
Elements UI / Overlays / AlertDialog

AlertDialog

A modal that interrupts with something important and waits for an answer.

Basic

A press outside does nothing; Escape or Cancel closes it. Focus starts on Cancel. onOpenChangeComplete fires when the enter and exit animations end.

actions: changes: reasons: details: complete:
Code
<AlertDialog title="Are you absolutely sure?"
             description="This action cannot be undone. This will permanently delete your account and remove your data from our servers.">
  <Button slot="trigger">Show dialog</Button>
  <AlertDialogFooter>
    <AlertDialogCancel>Cancel</AlertDialogCancel>
    <AlertDialogAction onclick={() => remove(account)}>Continue</AlertDialogAction>
  </AlertDialogFooter>
</AlertDialog>

Usage

import { AlertDialog, AlertDialogFooter, AlertDialogCancel, AlertDialogAction } from "@elements/ui/alert-dialog";

<AlertDialog title="Delete project?" description="This permanently deletes the project and its files.">
  <Button slot="trigger" variant="danger">Delete project</Button>
  <AlertDialogFooter>
    <AlertDialogCancel>Cancel</AlertDialogCancel>
    <AlertDialogAction variant="danger" onclick={() => remove(project)}>Delete</AlertDialogAction>
  </AlertDialogFooter>
</AlertDialog>

<!-- controlled: open is two-way -->
<AlertDialog open={state.confirming} title="Discard changes?">...</AlertDialog>

Examples

Small

size="sm": narrow and centered, with the two actions side by side.

Code
<AlertDialog size="sm" title="Allow accessory to connect?"
             description="Do you want to allow the USB accessory to connect to this device?">
  <Button slot="trigger">Show small dialog</Button>
  <AlertDialogFooter>
    <AlertDialogCancel>Don't allow</AlertDialogCancel>
    <AlertDialogAction>Allow</AlertDialogAction>
  </AlertDialogFooter>
</AlertDialog>

Media

AlertDialogMedia in the media slot: an icon beside the title from 40rem up, above it on phones.

Code
<AlertDialog title="Share this project?"
             description="Anyone with the link will be able to view and edit this project.">
  <Button slot="trigger">Share project</Button>
  <AlertDialogMedia slot="media"><Icon name="external-link" /></AlertDialogMedia>
  <AlertDialogFooter>
    <AlertDialogCancel>Cancel</AlertDialogCancel>
    <AlertDialogAction>Share</AlertDialogAction>
  </AlertDialogFooter>
</AlertDialog>

Small with media

The sm size keeps the media centered above the title.

Code
<AlertDialog size="sm" title="Turn on notifications?" description="...">
  <Button slot="trigger">Notifications</Button>
  <AlertDialogMedia slot="media"><Icon name="bell" /></AlertDialogMedia>
  ...
</AlertDialog>

Destructive

A danger Action and danger media for an action that cannot be undone.

Code
<AlertDialog size="sm" title="Delete chat?" description="This will permanently delete this chat conversation.">
  <Button slot="trigger" variant="danger">Delete chat</Button>
  <AlertDialogMedia slot="media" variant="danger"><Icon name="trash" /></AlertDialogMedia>
  <AlertDialogFooter>
    <AlertDialogCancel>Cancel</AlertDialogCancel>
    <AlertDialogAction variant="danger">Delete</AlertDialogAction>
  </AlertDialogFooter>
</AlertDialog>

Close after async work

The Action calls e.preventDefault() to stay open, shows loading at the same size, then the page closes the dialog. Escape is ignored while it runs.

deleted: 0
Code
<AlertDialog open={state.open} title="Delete 12 files?"
             onEscapeKeyDown={(e) => guard(state, e)}>
  <Button slot="trigger" variant="danger">Delete files</Button>
  <AlertDialogFooter>
    <AlertDialogCancel disabled={state.deleting}>Cancel</AlertDialogCancel>
    <AlertDialogAction variant="danger" loading={state.deleting}
                       onclick={(e) => remove(state, e)}>Delete</AlertDialogAction>
  </AlertDialogFooter>
</AlertDialog>

async function remove(state, e) {
  e.preventDefault();          // stay open while the work runs
  state.deleting = true;
  await rpc.deleteFiles();
  state.deleting = false;
  state.open = false;
}

Title and description parts

AlertDialogHeader, AlertDialogTitle and AlertDialogDescription take rich content and are wired to aria-labelledby and aria-describedby.

Code
<AlertDialog>
  <Button slot="trigger">Leave page</Button>
  <AlertDialogHeader>
    <AlertDialogTitle>Leave <em>Untitled draft</em>?</AlertDialogTitle>
    <AlertDialogDescription>Your changes have not been saved.</AlertDialogDescription>
  </AlertDialogHeader>
  ...
</AlertDialog>

From a dialog

An alert dialog opened inside a dialog. Escape closes only the alert, and focus returns to the button that opened it.

Code
<Dialog title="Edit document">
  <Button slot="trigger">Edit document</Button>
  <textarea>...</textarea>
  <DialogFooter>
    <AlertDialog title="Discard changes?" description="Your edits will be lost.">
      <Button slot="trigger" variant="ghost">Discard</Button>
      ...
    </AlertDialog>
  </DialogFooter>
</Dialog>

No visible title

With no title, the label attribute names the dialog with aria-label, and the description is still the alert message wired to aria-describedby.

Code
<AlertDialog label="Session expiring" description="Your session expires in 2 minutes. Stay signed in?">
  <Button slot="trigger">Session warning</Button>
  ...
</AlertDialog>

Focus on open and close

initialFocus names the element focused on open in place of Cancel; use it when the action is safe. returnFocus={false} leaves focus where it lands on close. onEscapeKeyDown runs before Escape closes, and preventDefault would keep it open.

Code
<AlertDialog title="Publish this post?" description="..."
             initialFocus="[data-alert-dialog-action]" returnFocus={false}
             onEscapeKeyDown={(e) => log("escape")}>
  <Button slot="trigger">Publish</Button>
  <AlertDialogFooter>
    <AlertDialogCancel>Not yet</AlertDialogCancel>
    <AlertDialogAction>Publish</AlertDialogAction>
  </AlertDialogFooter>
</AlertDialog>

Confirm before discarding

The WAI-ARIA example: Discard asks first. No closes without changes, Yes empties the notes. Focus starts on No, the least destructive answer, and finalFocus sends it back to the notes field on close. With the notes empty Discard is aria-disabled: it stays in the Tab order but does nothing.

Code
<textarea aria-label="Notes" value={state.notes} oninput={(e) => state.notes = e.target.value}></textarea>
<AlertDialog title="Confirmation" description="Are you sure you want to discard all of your notes?"
             finalFocus="[data-notes]">
  <!-- aria-disabled: Discard stays in the Tab order once the notes are empty -->
  <Button slot="trigger" disabled={!state.notes} focusableWhenDisabled>Discard</Button>
  <AlertDialogFooter>
    <AlertDialogCancel>No</AlertDialogCancel>
    <AlertDialogAction variant="danger" onclick={() => state.notes = ""}>Yes</AlertDialogAction>
  </AlertDialogFooter>
</AlertDialog>

Detached triggers

Buttons anywhere on the page open one controlled alert dialog and pass what it is about through page state. Focus returns to the button used. Opening it from page state is the caller's change, so onOpenChange reports only the dialog's own close.

reasons: complete:
Code
<Button onclick={() => discard(state, "Q3 plan")}>Discard Q3 plan</Button>
<Button onclick={() => discard(state, "Hiring memo")}>Discard Hiring memo</Button>

<AlertDialog open={state.open} title={`Discard ${state.doc}?`}
             description="The draft is deleted for everyone."
             onOpenChange={(open, reason) => log(reason)}>
  <AlertDialogFooter>
    <AlertDialogCancel>Cancel</AlertDialogCancel>
    <AlertDialogAction variant="danger">Discard</AlertDialogAction>
  </AlertDialogFooter>
</AlertDialog>

function discard(state, doc) {
  state.doc = doc;      // the payload
  state.open = true;    // focus returns to the button used
}

Open from a menu

A menu item opens a controlled alert dialog. The item is gone by the time it closes, so focus goes back to the menu's button.

playlists: 3
Code
<DropdownMenu>
  <Button slot="trigger">Playlist</Button>
  <DropdownMenuItem onSelect={() => state.deleting = true}>Delete playlist...</DropdownMenuItem>
</DropdownMenu>
<AlertDialog open={state.deleting} title="Delete playlist?" description="...">
  ...
</AlertDialog>

Focus events

onOpenAutoFocus and onCloseAutoFocus run before focus moves; preventDefault() leaves focus to the handler. Here Archive takes focus on open, and focus stays off the trigger on close.

closeFocus: 0
Code
<AlertDialog title="Archive 3 threads?"
             onOpenAutoFocus={(e) => { e.preventDefault(); archive.focus(); }}
             onCloseAutoFocus={(e) => e.preventDefault()}>
  ...
</AlertDialog>

Focus by interaction type

initialFocus and finalFocus can be functions of how the alert opened or closed: keyboard, mouse, touch or pen. Returning true keeps the default, Cancel.

Code
<AlertDialog title="Discard the import?" description="Name the batch to keep it as a draft instead."
             initialFocus={(type) => type === "keyboard" ? "[data-batch]" : true}
             finalFocus={(type) => type === "keyboard" ? true : "[data-after]"}>
  <Button slot="trigger">Discard import</Button>
  <input aria-label="Batch name" data-batch>
  <AlertDialogFooter>
    <AlertDialogCancel>Cancel</AlertDialogCancel>
    <AlertDialogAction variant="danger">Discard</AlertDialogAction>
  </AlertDialogFooter>
</AlertDialog>

Open on load

open={true} without a binding: the alert is open when the page loads, then owns its state. Cancel closes it and the trigger opens it again.

Code
<AlertDialog open={true} title="Your trial ends today" description="Add a payment method to keep your projects running.">
  <Button slot="trigger">Trial notice</Button>
  ...
</AlertDialog>

Confirm with a form

A form inside the alert: the Action is its submit button, so a click or Enter in the field submits it.

submits: 0
Code
<AlertDialog title="Delete repository?" description="Type the name to confirm.">
  <Button slot="trigger" variant="danger">Delete repository</Button>
  <form onsubmit={(e) => remove(e)}>
    <input name="confirm" aria-label="Repository name">
    <AlertDialogFooter>
      <AlertDialogCancel>Cancel</AlertDialogCancel>
      <AlertDialogAction variant="danger" type="submit">Delete</AlertDialogAction>
    </AlertDialogFooter>
  </form>
</AlertDialog>

Escape reaches the page

onEscapeKeyDown can call allowPropagation(): Escape closes the alert and still reaches the page's own key handlers.

Code
<AlertDialog title="Stop the upload?" description="Files sent so far are kept." onEscapeKeyDown={(e) => e.allowPropagation()}>
  <Button slot="trigger">Stop upload</Button>
  <AlertDialogFooter>
    <AlertDialogCancel>Keep uploading</AlertDialogCancel>
    <AlertDialogAction>Stop</AlertDialogAction>
  </AlertDialogFooter>
</AlertDialog>

Right to left

Under dir="rtl" the header aligns to the start edge and the actions mirror.

Code
<div dir="rtl">
  <AlertDialog title="هل أنت متأكد تماما؟" description="لا يمكن التراجع عن هذا الإجراء.">
    <Button slot="trigger">إظهار الحوار</Button>
    ...
  </AlertDialog>
</div>
Reference AlertDialog in the manual Every part, attribute, slot, key and data attribute.
Prev ToggleGroupNext Command
Product
Manual Pricing Compare
Company
History Contact
Legal
Privacy Policy Acceptable Use Terms License Agreement
© Elements Dev Corporation 2026