HTML <dialog> Tag
Learn the HTML <dialog> tag: show() vs showModal(), the form method="dialog" pattern, close and cancel events, and accessibility.
The <dialog> tag is one of the HTML5 elements. It creates a native dialog box — a pop-up panel such as a confirmation message, an alert, or a form — that the user interacts with before continuing. The dialog is hidden by default and is shown and hidden through a small JavaScript API.
This page covers the two ways to open a dialog (show() and showModal()), how to close one and read its result, the <form method="dialog"> pattern, the close and cancel events, accessibility, and styling the backdrop.
Why use native <dialog> instead of a custom <div> modal?
Before <dialog>, developers hand-built modals out of a <div> plus a stack of CSS and JavaScript. The native element does that work for you and does it more reliably:
- Built-in focus trapping. When opened with
showModal(), keyboard focus (Tab / Shift+Tab) is kept inside the dialog. A<div>modal has to trap focus manually, and getting it wrong is a common accessibility bug. - Top layer stacking. A modal dialog renders in the browser's top layer, so it always paints above the rest of the page regardless of
z-index. No morez-indexwars. - Automatic backdrop.
showModal()draws a::backdropbehind the dialog that blocks clicks on the page underneath — no extra overlay element needed. - Escape to close. Pressing Esc closes a modal dialog automatically (firing a
cancelevent you can react to). - Real semantics. The element carries an implicit
role="dialog", so assistive technology announces it correctly without extra ARIA.
Syntax
The <dialog> tag comes in pairs. Put the dialog content between the opening (<dialog>) and closing (</dialog>) tags. You open it from script with one of two methods on the element's DOM object:
dialog.show()— opens a non-modal dialog.dialog.showModal()— opens a modal dialog.dialog.close([returnValue])— closes the dialog and optionally records a return value.
The boolean open attribute reflects whether the dialog is currently shown. You generally do not set it by hand; the methods above manage it for you.
Non-modal dialogs: show()
show() opens the dialog in place, without blocking the page. The rest of the document stays interactive, there is no ::backdrop, focus is not trapped, and Esc does not close it. This is the right choice for non-critical, dismissible panels such as a small toolbox, a "what's new" note, or an inline picker.
<!DOCTYPE html>
<html>
<head>
<title>Non-modal dialog with show()</title>
</head>
<body>
<button id="open">Open non-modal dialog</button>
<dialog id="info">
<p>You can still click and scroll the page behind me.</p>
<button id="close">Close</button>
</dialog>
<script>
const dialog = document.getElementById("info");
document.getElementById("open").addEventListener("click", () => {
dialog.show();
});
document.getElementById("close").addEventListener("click", () => {
dialog.close();
});
</script>
</body>
</html>Modal dialogs: showModal()
showModal() opens the dialog as a modal. This changes the behavior in four important ways:
- Interaction is blocked. Everything outside the dialog becomes inert — clicks and keyboard input cannot reach the page behind it.
- A backdrop appears. The browser renders the
::backdroppseudo-element behind the dialog, which you can style. - Focus is trapped. Tabbing cycles only through the focusable elements inside the dialog.
- It joins the top layer. The dialog paints above all other content, ignoring
z-index. Pressing Esc closes it.
<!DOCTYPE html>
<html>
<head>
<title>Modal dialog with showModal()</title>
<style>
dialog {
width: 40%;
border: 1px solid #ccc;
border-radius: 0.5em;
padding: 1em;
}
dialog::backdrop {
background-color: rgba(0, 0, 0, 0.5);
}
</style>
</head>
<body>
<button id="open">Open modal dialog</button>
<dialog id="confirm">
<p>The page behind is blocked. Press Esc or a button to close.</p>
<button id="close">Close dialog</button>
</dialog>
<script>
const dialog = document.getElementById("confirm");
document.getElementById("open").addEventListener("click", () => {
dialog.showModal();
});
document.getElementById("close").addEventListener("click", () => {
dialog.close();
});
</script>
</body>
</html>The <form method="dialog"> pattern
A <form> inside a <dialog> can use method="dialog". Submitting such a form closes the dialog without sending a network request, and the value of the submit <button> that was clicked is stored in the dialog's returnValue DOM property. This makes it easy to tell which button the user chose.
<!DOCTYPE html>
<html>
<head>
<title>Dialog with a form</title>
</head>
<body>
<button id="open">Delete file</button>
<p id="result"></p>
<dialog id="confirm">
<form method="dialog">
<p>Are you sure you want to delete this file?</p>
<button value="cancel">Cancel</button>
<button value="delete">Delete</button>
</form>
</dialog>
<script>
const dialog = document.getElementById("confirm");
const result = document.getElementById("result");
document.getElementById("open").addEventListener("click", () => {
dialog.showModal();
});
dialog.addEventListener("close", () => {
// returnValue is the value of the button that submitted the form
result.textContent = "You chose: " + dialog.returnValue;
});
</script>
</body>
</html>returnValue is a property of the DOM element, not an HTML attribute — you read or set it from JavaScript. You can also set it directly when closing in code: dialog.close("delete").
The close and cancel events
A <dialog> fires two events you can listen for:
| Event | When it fires |
|---|---|
close | Whenever the dialog is closed — by close(), by submitting a method="dialog" form, or by Esc. Read returnValue here. |
cancel | When the user dismisses a modal dialog with the Esc key. Fires before close. Call event.preventDefault() to keep the dialog open. |
<dialog id="editor">
<p>Unsaved changes — Esc would normally close me.</p>
<button onclick="this.closest('dialog').close()">Done</button>
</dialog>
<script>
const dialog = document.getElementById("editor");
dialog.addEventListener("cancel", (event) => {
// Prevent Esc from discarding unsaved work
event.preventDefault();
});
dialog.addEventListener("close", () => {
console.log("Dialog closed");
});
</script>For more on wiring up these listeners, see JavaScript Events and the JavaScript HTML DOM.
Accessibility
A <dialog> has an implicit role="dialog", but you still need to give it an accessible name and manage focus:
- Name the dialog. Point
aria-labelledbyat the id of the dialog's heading so screen readers announce its title. Usearia-describedbyfor a longer description. - Focus on open.
showModal()moves focus into the dialog automatically — onto the first focusable element, or an element you mark with theautofocusattribute. Prefer placingautofocuson a safe control (like Cancel) rather than a destructive one. - Return focus on close. When the dialog closes, return focus to the control that opened it so keyboard users are not left at the top of the page. With native
<dialog>, the browser does this for you; for the safest results, store and restore the trigger yourself.
<button id="open">Edit profile</button>
<dialog id="profile" aria-labelledby="profile-title" aria-describedby="profile-desc">
<h2 id="profile-title">Edit profile</h2>
<p id="profile-desc">Update your display name, then save.</p>
<form method="dialog">
<input type="text" aria-label="Display name" autofocus>
<button value="save">Save</button>
</form>
</dialog>
<script>
const openButton = document.getElementById("open");
const dialog = document.getElementById("profile");
openButton.addEventListener("click", () => dialog.showModal());
// Explicitly return focus to the trigger when the dialog closes
dialog.addEventListener("close", () => openButton.focus());
</script>See DOM accessibility considerations for more on building accessible interactive widgets.
Styling the dialog and its backdrop
You can style the dialog box itself and, for modal dialogs, the area behind it with the ::backdrop pseudo-element.
dialog {
border: 1px solid #ccc;
border-radius: 0.5em;
padding: 1em;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}
/* Only rendered for dialogs opened with showModal() */
dialog::backdrop {
background-color: rgba(0, 0, 0, 0.5);
}Attributes
| Attribute | Value | Description |
|---|---|---|
open | open | Boolean attribute indicating the dialog is currently shown. Usually set for you by show() / showModal(). |
The <dialog> tag also supports the Global Attributes and the Event Attributes.