HTML Dialog Table
| Piece | What it does | Field note |
|---|---|---|
<dialog> | Native modal element | Browser-managed: focus trap, Esc close, top-layer render - free |
dialog.showModal() | Opens as modal (blocking) | Only ONE modal at a time; ::backdrop pseudo-element automatic |
dialog.show() | Opens as non-modal | Inline mode - no backdrop, no focus trap, no Esc |
dialog.close(returnValue) | Closes with optional value | The close event fires; returnValue readable by caller |
::backdrop | The overlay behind the modal | Style with CSS: background, backdrop-filter: blur() |
dialog::backdrop { backdrop-filter: blur(4px) } | Frosted glass effect | GPU-composited, no JS needed |
form method="dialog" | Close form inside dialog | The native form-dialog contract: submit returns form data |
cancel event (Esc) | Fires before Esc close | preventDefault() to block Esc - confirm-discard pattern |
The native dialog element solves the four problems every JS modal library reinvents: focus trapping (browser-managed), Esc to close (the cancel event), top-layer rendering (no z-index lottery), and ::backdrop (a real pseudo-element, not a wrapper div). The table below is the working eight.
Bottom line: the JS modal library era is over. showModal() plus CSS ::backdrop plus form method=dialog covers every use case the libraries solved, without a single dependency. The browser manages focus (traps it inside, returns it on close), renders in the top layer (above all z-index), and fires the right events at the right time.
The honest part: two events control the dialog's lifecycle. close fires when the dialog successfully closes (confirm, form submit) - read returnValue for the user's choice. cancel fires when the user presses Esc - call preventDefault() to block the close and show a confirm-discard prompt instead. Handling both events is the difference between a dialog that loses user data and one that doesn't.
How to use
- Use showModal() to open as modal (focus trap + backdrop + top layer) or show() for inline mode.
- Style ::backdrop with CSS - the overlay is a real pseudo-element, not a wrapper div.
- Use form method="dialog" for the native close contract: the submit button closes the dialog and populates returnValue.
Frequently asked questions
What does showModal() give me that show() doesn't?
showModal() makes the dialog MODAL: it renders in the top layer (above all z-index, no stacking context lottery), traps focus inside the dialog, adds inert behaviour to the page behind it, enables the ::backdrop pseudo-element, and wires the Esc key to fire the cancel event. show() opens the dialog inline - no backdrop, no focus trap, no Esc handling. The choice is modal-vs-inline, and most use cases are modal.
How do I prevent the Esc key from closing my dialog?
Listen for the cancel event and call preventDefault(): dialog.addEventListener('cancel', e => { if (hasUnsavedChanges) e.preventDefault(); }). The cancel event fires when the user presses Esc (or uses the browser's back gesture on some platforms). After preventing, show a confirm-discard prompt. The close event (which fires on successful close) does not have a preventDefault - by that point, the dialog is already closing.
How does form method="dialog" work?
A form inside a dialog with method="dialog" closes the dialog on submit and populates dialog.returnValue with the submit button's value. The pattern for confirm dialogs: two buttons with values "confirm" and "cancel" - after close, dialog.returnValue tells you which button the user pressed. No JavaScript submit handler needed; the browser manages the contract.
Do I still need a JS modal library?
Only if you need features the native dialog doesn't provide: draggable dialogs, stacked multiple modals, or animations on the backdrop (the ::backdrop pseudo-element has limited animation support in older browsers). For standard confirm/alert/form dialogs, the native element covers everything with zero dependencies, better accessibility (focus trap and screen reader announcements built in), and better performance (top-layer rendering bypasses the normal stacking context).