HTML Dialog Table

PieceWhat it doesField note
<dialog>Native modal elementBrowser-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-modalInline mode - no backdrop, no focus trap, no Esc
dialog.close(returnValue)Closes with optional valueThe close event fires; returnValue readable by caller
::backdropThe overlay behind the modalStyle with CSS: background, backdrop-filter: blur()
dialog::backdrop { backdrop-filter: blur(4px) }Frosted glass effectGPU-composited, no JS needed
form method="dialog"Close form inside dialogThe native form-dialog contract: submit returns form data
cancel event (Esc)Fires before Esc closepreventDefault() to block Esc - confirm-discard pattern
Reference: the MDN dialog element reference. The native dialog 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). 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. Related tools: forms table (the dialog form contract), timers table for delayed dialogs, and DOM traversal table for reading the dialog content.

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

  1. Use showModal() to open as modal (focus trap + backdrop + top layer) or show() for inline mode.
  2. Style ::backdrop with CSS - the overlay is a real pseudo-element, not a wrapper div.
  3. 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).

Related tools