A modal dialog that interrupts the user with important content and expects a response.
Are you absolutely sure?
This action cannot be undone. This will permanently delete your account and remove your data from our servers.
<AlertDialog.root id="alert-dialog-primitive">
<AlertDialog.trigger
id="alert-dialog-trigger"
class="DemoButton violet"
>
Delete account
</AlertDialog.trigger>
<AlertDialog.portal id="alert-dialog-portal">
<AlertDialog.overlay class="DemoAlertDialogOverlay" />
<AlertDialog.content
id="alert-dialog-content"
class="DemoAlertDialogContent"
>
<AlertDialog.title class="DemoAlertDialogTitle">Are you absolutely sure?</AlertDialog.title>
<AlertDialog.description class="DemoAlertDialogDescription">
This action cannot be undone. This will permanently delete your account and remove your data from our servers.
</AlertDialog.description>
<div style="display: flex; gap: 25px; justify-content: flex-end;">
<AlertDialog.cancel class="DemoButton mauve">Cancel</AlertDialog.cancel>
<AlertDialog.action class="DemoButton red">Yes, delete account</AlertDialog.action>
</div>
</AlertDialog.content>
</AlertDialog.portal>
</AlertDialog.root>
/* reset */
button {
all: unset;
}
.DemoAlertDialogOverlay {
background-color: var(--black-a9);
position: fixed;
inset: 0;
animation: overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1);
}
.DemoAlertDialogContent {
background-color: var(--gray-1);
border-radius: 6px;
box-shadow: var(--shadow-6);
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90vw;
max-width: 500px;
max-height: 85vh;
padding: 25px;
animation: contentShow 150ms cubic-bezier(0.16, 1, 0.3, 1);
}
.DemoAlertDialogContent:focus {
outline: none;
}
.DemoAlertDialogTitle {
margin: 0;
color: var(--mauve-12);
font-size: 17px;
font-weight: 500;
}
.DemoAlertDialogDescription {
margin-bottom: 20px;
color: var(--mauve-11);
font-size: 15px;
line-height: 1.5;
}
.DemoButton {
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 4px;
padding: 0 15px;
font-size: 15px;
line-height: 1;
font-weight: 500;
height: 35px;
user-select: none;
}
.DemoButton:focus:not(:focus-visible) {
outline: 0;
}
.DemoButton:focus-visible {
outline: 2px solid var(--violet-6);
outline-offset: 1px;
}
.DemoButton.violet {
background-color: var(--violet-4);
color: var(--violet-12);
outline-color: var(--violet-6);
}
.DemoButton.violet:hover {
background-color: var(--mauve-3);
}
.DemoButton.red {
background-color: var(--red-4);
color: var(--red-11);
outline-color: var(--red-7);
}
.DemoButton.red:hover {
background-color: var(--red-5);
}
.DemoButton.mauve {
background-color: var(--mauve-4);
color: var(--mauve-11);
outline-color: var(--mauve-7);
}
.DemoButton.mauve:hover {
background-color: var(--mauve-5);
}
@keyframes overlayShow {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes contentShow {
from {
opacity: 0;
transform: translate(-50%, -48%) scale(0.96);
}
to {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
}
Anatomy
Import all parts and piece them together.
<AlertDialog.root>
<AlertDialog.trigger />
<AlertDialog.portal>
<AlertDialog.overlay />
<AlertDialog.content>
<AlertDialog.title />
<AlertDialog.description />
<AlertDialog.cancel />
<AlertDialog.action />
</AlertDialog.content>
</AlertDialog.portal>
</AlertDialog.root>
Anatomy
-
Root - Contains all alert dialog parts.
-
Trigger - The button that opens the dialog.
-
Portal - Portals overlay and content into the document body.
-
Overlay - Dimmed layer behind the content.
-
Content - Contains title, description, and action buttons.
-
Title - Accessible name announced when the dialog opens.
-
Description - Accessible description announced when the dialog opens.
-
Cancel - Dismisses the dialog without confirming the action.
-
Action - Confirms the destructive or irreversible action and closes the dialog.
API Reference
Root
Contains all the parts of an alert dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
default_open
|
boolean
|
false
|
|
id
|
string
|
—
|
|
on_open_change
|
string
|
nil
|
|
open
|
boolean
|
false
|
default_open
boolean
Default
false
id
string
Default
—
on_open_change
string
Default
nil
open
boolean
Default
false
Data attributes
| Attribute | Values | Description |
|---|---|---|
[data-state]
|
open | closed
|
Present on the root reflecting open state. |
Trigger
A button that opens the dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
id
|
string
|
nil
|
id
string
Default
nil
Data attributes
| Attribute | Values | Description |
|---|---|---|
[data-state]
|
open | closed
|
Reflects whether the dialog is open. |
Portal
When used, portals your overlay and content parts into the target (default body).
| Prop | Type | Default | Description |
|---|---|---|---|
id
|
string
|
—
|
|
target
|
string
|
"body"
|
id
string
Default
—
target
string
Default
"body"
Overlay
A layer that covers the inert portion of the view when the dialog is open.
| Prop | Type | Default | Description |
|---|---|---|---|
id
|
string
|
nil
|
id
string
Default
nil
Data attributes
| Attribute | Values | Description |
|---|---|---|
[data-state]
|
open | closed
|
Reflects whether the dialog is open. |
Content
Contains content to be rendered when the dialog is open.
| Prop | Type | Default | Description |
|---|---|---|---|
id
|
string
|
nil
|
id
string
Default
nil
Data attributes
| Attribute | Values | Description |
|---|---|---|
[data-state]
|
open | closed
|
Reflects whether the dialog is open. |
Title
An accessible name to be announced when the dialog is opened. Alternatively, provide aria-label or aria-labelledby on AlertDialog.content and omit this part.
| Prop | Type | Default | Description |
|---|---|---|---|
id
|
string
|
nil
|
id
string
Default
nil
Description
An accessible description to be announced when the dialog is opened. Alternatively, provide aria-describedby on AlertDialog.content and omit this part.
| Prop | Type | Default | Description |
|---|---|---|---|
id
|
string
|
nil
|
id
string
Default
nil
Cancel
A button that closes the dialog. Distinguish this visually from AlertDialog.action buttons.
| Prop | Type | Default | Description |
|---|---|---|---|
id
|
string
|
nil
|
id
string
Default
nil
Action
A button that closes the dialog after confirming the action. Distinguish this visually from AlertDialog.cancel.
| Prop | Type | Default | Description |
|---|---|---|---|
id
|
string
|
nil
|
id
string
Default
nil
Examples
Close after asynchronous form submission
Use controlled open and on_open_change to close the alert dialog after an async LiveView event completes.
<AlertDialog.root id="async-alert" open={@open} on_open_change="alert_open_change">
<AlertDialog.trigger id="async-alert-trigger" class="DemoButton violet">
Delete account
</AlertDialog.trigger>
<AlertDialog.portal id="async-alert-portal">
<AlertDialog.overlay class="DemoAlertDialogOverlay" />
<AlertDialog.content id="async-alert-content" class="DemoAlertDialogContent">
<AlertDialog.title class="DemoAlertDialogTitle">Delete account</AlertDialog.title>
<AlertDialog.description class="DemoAlertDialogDescription">
This action cannot be undone.
</AlertDialog.description>
<form phx-submit="delete_account">
…
<div style="display: flex; gap: 25px; justify-content: flex-end;">
<AlertDialog.cancel class="DemoButton mauve">Cancel</AlertDialog.cancel>
<button type="submit" class="DemoButton red">Yes, delete account</button>
</div>
</form>
</AlertDialog.content>
</AlertDialog.portal>
</AlertDialog.root>
def handle_event("alert_open_change", %{"open" => open}, socket) do
{:noreply, assign(socket, :open, open)}
end
def handle_event("delete_account", _params, socket) do
# Perform async work, then close the dialog
{:noreply, assign(socket, :open, false)}
end
Custom portal target
Portal into a specific container via target:
<AlertDialog.portal id="alert-dialog-portal" target="#app-portal-root">
<AlertDialog.overlay class="DemoAlertDialogOverlay" />
<AlertDialog.content id="alert-dialog-content" class="DemoAlertDialogContent">
…
</AlertDialog.content>
</AlertDialog.portal>
Accessibility
Adheres to the Alert and Message Dialogs WAI-ARIA design pattern.
Keyboard Interactions
| Key | Description |
|---|---|
Space
|
Opens/closes the dialog. |
Enter
|
Opens/closes the dialog. |
Tab
|
Moves focus to the next focusable element. |
Shift + Tab
|
Moves focus to the previous focusable element. |
Escape
|
Closes the dialog and returns focus to the trigger. |