Generate a native HTML <dialog> with the right opener — showModal() for a modal that traps focus and blocks the page, or show() for a non-modal panel. Add a method="dialog" form so a submit button auto-closes and returns returnValue, style the ::backdrop, animate entry with @starting-style, and choose how ESC and outside-click behave.
<dialog> support…The live dialog runs in this page — close it with the rendered buttons, ESC (if allowed), or the backdrop click toggle.
showModal() | show() | |
|---|---|---|
| Page interaction | Blocked — inert outside. | Page stays interactive. |
::backdrop | Yes — rendered in top layer. | No backdrop pseudo. |
| Focus trap | Yes — Tab cycles inside. | No — Tab can leave. |
| ESC closes | Yes by default (cancel event). | No, unless closedby="any". |
| Top layer | Yes — above everything. | No — normal stacking. |
| Use for | Confirmations, sign-in, blocking flows. | Toasts, side panels, non-blocking pickers. |
A form with method="dialog" inside a <dialog> does not submit anywhere. When the user clicks a submit button, the dialog closes and dialog.returnValue is set to the button's value. Listen with the close event:
dlg.addEventListener('close', () => {
if (dlg.returnValue === 'delete') doDelete();
});
Use formmethod="dialog" on a single button to scope this behavior without wrapping the whole form.
| API | What |
|---|---|
dlg.show() | Open non-modal. |
dlg.showModal() | Open modal — in top layer with ::backdrop. |
dlg.close(value?) | Close it; optional value becomes returnValue. |
dlg.returnValue | Last close value (string). |
dlg.open | Boolean — currently open? |
cancel event | Fires when user presses ESC. Cancellable. |
close event | Fires after dialog closes (any reason). |
showModal() & ::backdrop: Baseline since 2022 — Chrome 37+, Firefox 98+, Safari 15.4+.closedby attribute: Chrome 134+, Edge 134+. Falls back to default behavior in older browsers.@starting-style entry animation: Chrome 117+, Safari 17.5+, Firefox 129+.