Home HTML

The HTML <dialog> element

<dialog> is the native HTML modal and non-modal dialog element. It manages focus trapping, the top layer, and inert background, replacing many ad-hoc modal libraries.

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 as inert, 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:

  1. The dialog is placed in the top layer, escaping overflow and z-index constraints.
  2. The rest of the document tree is marked inert, so click and key events outside the dialog are dropped.
  3. Focus moves into the dialog. The first focusable element with the autofocus attribute, or the first tabbable element otherwise, receives focus.
  4. The Escape key dispatches cancel, then close. If closedby is set to a value other than none, dismissal is also wired.
  5. 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

  • hidden attribute on <dialog>. A hidden dialog cannot be shown via showModal() until the attribute is removed. Toggle open/close via the API, not by hiding.
  • Stacking <dialog> inside scrollable container. The top layer escapes overflow; designs that depended on clipping behaviour will surprise authors.
  • <form> outside the dialog. Forms must be inside the dialog for method="dialog" to dismiss correctly.
  • Custom backdrop without ::backdrop. Avoid duplicating the scrim with an extra DOM node; use dialog::backdrop styling.
  • Focus return. As above, the user agent does not return focus on close; authors must do so.

Further reading