Reference guide · html-css-js · Published 2026-08-16 · 4 min read
The HTML dialog element
The HTML dialog element: show versus showModal, the form method, focus management, accessibility, and browser support.
- ·Modes of dialog
- ·Open and close
- ·Accessibility behaviour
What the native dialog is
The <dialog> element is HTML's built-in modal container. It saves you from building a modal from scratch (a layer above everything with focus management and escape handling glued on by hand). A dialog is a box that can appear above the page, support a heading, text, form controls, and buttons, and stay inline in the document when closed.
It works with everything else you already write, and it is one of the HTML elements that carries real semantics instead of being a generic wrapper.
Two modes: inline and modal
The crucial concept is that dialog has two behaviours:
dialog.show()opens it as a normal part of the page flow, without a top layer, without a backdrop, and without focus trapping. It behaves like any other element that becomes visible.dialog.showModal()opens it as a top-layer modal with a backdrop, and it traps focus and disables the rest of the document.
Most interactive dialogs should use showModal(), because that is the semantics users expect from a dialog: everything else is inactive behind it. The difference is not cosmetic: showModal() also applies the top layer, so the dialog paints above every positioned element in the document.
Open, close, and the form method
| Action | Code | Notes |
|---|---|---|
| Open as page flow | dialog.show() | No backdrop, no focus trap |
| Open as modal | dialog.showModal() | Backdrop, focus trap, top layer |
| Close | dialog.close([value]) | Fires close event, optional return value |
| Close via Esc | Built in | Only when the dialog is open |
const dlg = document.querySelector('#confirm');
dlg.showModal();
A form inside a dialog with method="dialog" closes the dialog when submitted and returns its value, which is the clean way to make a confirm dialog:
<dialog id="confirm">
<form method="dialog">
<button value="yes">Yes</button>
<button value="no">No</button>
</form>
</dialog>
The close event tells you the dialog closed with newValue, and the cancel event fires for the Esc key (closing on Esc is built-in). For the full behaviour, including an explicit "cancel not close" pattern, the forms guide covers the related form patterns.
Focus, roles, and accessibility
showModal() puts the dialog and its controls in an inert top layer and moves focus to the first focusable element inside, which is most of what makes a custom modal a pain. The native element means you do not hand-write the focus trapping, and screen readers announce the dialog's role.
You still do a little yourself: give the dialog a label with aria-labelledby to a heading inside it, and keep the focusable area scripted so it stays small. The ARIA guide has the full ruleset, and an inert attribute on the dialog body (inert) is the recommended way to actually make the background unclickable even when you do not open it with showModal().
Support and the fallback
<dialog> is supported in every modern browser, and the fallback path is a tiny bit of script that calls show() for browsers that do not implement showModal(), or a simple feature check before attaching the handlers. The element is safe to ship today, and it removes the biggest maintainability risk of hand-rolled modals: a dialog written once that re-applies focus and click logic every time you need another one.
Prevention
Use showModal() for anything you would call a modal, keep method="dialog" form submissions inside, always label the dialog with aria-labelledby, and reserve show() for inline, non-modal popovers. That is the whole mental model, and it is the setup that keeps modals accessible for free instead of by accident.