What <dialog> is
<dialog> is a native HTML element that represents a dialog box or
other interactive component. Two activation modes are defined:
dialog.show()opens the dialog as a non-modal dialog. The page behind remains interactive.dialog.showModal()opens the dialog as a modal. The browser marks the rest of the page asinert, traps focus inside the dialog, and renders the dialog in the top layer above all other page content.
The element ships with a ::backdrop pseudo-element that styles the
modal scrim. It supports the closedby attribute (HTML 2024), which
declares whether the dialog can be dismissed by clicking outside or
pressing Escape (closedby="any", closedby="closerequest",
closedby="none").
Markup
<button id="open">Edit profile</button>
<dialog id="d" aria-labelledby="d-title">
<h2 id="d-title">Edit profile</h2>
<form method="dialog">
<label>Display name <input name="name" required /></label>
<button value="save">Save</button>
<button value="cancel" formnovalidate>Cancel</button>
</form>
</dialog>
<script type="module">
document.getElementById('open').addEventListener('click', () => {
document.getElementById('d').showModal();
});
</script>
The <form method="dialog"> pattern dismisses the dialog when any
submit button inside is activated, returning the activated button’s
value via dialog.returnValue.
What the user agent does for you
When showModal() is called:
- The dialog is placed in the top layer, escaping
overflowandz-indexconstraints. - The rest of the document tree is marked
inert, so click and key events outside the dialog are dropped. - Focus moves into the dialog. The first focusable element with the
autofocusattribute, or the first tabbable element otherwise, receives focus. - The Escape key dispatches
cancel, thenclose. Ifclosedbyis set to a value other thannone, dismissal is also wired. - Tab and Shift-Tab are constrained to the dialog’s tab order.
These behaviours replace the bulk of what custom modal libraries used to provide.
Accessibility considerations
<dialog> exposes role="dialog" by default, with the accessible
name computed from aria-labelledby (preferred) or aria-label. For
critical confirmations, set role="alertdialog" and ensure the body
is announced as the dialog opens. The first focusable element should
be the primary control of the dialog, not the close button.
When closing the dialog, return focus to the element that opened it.
The element that opened it is not automatically tracked by the user
agent; libraries or authors must store the opener and call
opener.focus() on the dialog’s close event.
Browser engine support
<dialog> reached cross-engine support in 2022 across Chromium
(Blink), WebKit, and Gecko. The closedby attribute reached
interop in 2024. The :modal and :open pseudo-classes are
supported in current versions of all three engines for distinguishing
modal from non-modal state in CSS.
Common pitfalls
hiddenattribute on<dialog>. A hidden dialog cannot be shown viashowModal()until the attribute is removed. Toggle open/close via the API, not by hiding.- Stacking
<dialog>inside scrollable container. The top layer escapesoverflow; designs that depended on clipping behaviour will surprise authors. <form>outside the dialog. Forms must be inside the dialog formethod="dialog"to dismiss correctly.- Custom backdrop without
::backdrop. Avoid duplicating the scrim with an extra DOM node; usedialog::backdropstyling. - Focus return. As above, the user agent does not return focus on close; authors must do so.
Further reading
- HTML Living Standard, §4.11 Interactive elements — the dialog element.
- Scott O’Hara’s review of the interop history (scottohara.me/blog/2019/03/05/open-dialog) explains why authors lived with
aria-modalpolyfills until 2022. - The
closedbyattribute proposal — merged 2024 — adds the missing dismiss-handling primitive. - Open UI’s popover-vs-dialog comparison is the deciding reference when an ad-hoc surface could go either way.