Skip to content
Carl Victor Fontanos
Carl Victor Fontanos

Carl Victor Fontanos

Software Engineer

I build web applications and share what I learn along the way.

© 2026

The <dialog> Element Probably Replaces Your Modal Library

C
Carlo Fontanos
· 2 min read

Modal libraries exist because building a correct modal by hand is genuinely hard: trap focus, restore focus on close, handle Escape, block the page behind, win the z-index war. The dialog element ships all of that in the browser, and it's been in every engine since 2022.

<dialog id="confirm">
    <h2>Delete this post?</h2>
    <p>This can't be undone.</p>
    <form method="dialog">
        <button value="cancel">Cancel</button>
        <button value="delete">Delete</button>
    </form>
</dialog>
const dialog = document.getElementById('confirm');
dialog.showModal();

dialog.addEventListener('close', () => {
    if (dialog.returnValue === 'delete') actuallyDelete();
});

The parts people miss

form method="dialog" is the hidden gem inside the hidden gem: submitting that form closes the dialog and sets dialog.returnValue to the clicked button's value. A complete confirm-flow with no click handlers on the buttons at all.

The top layer. showModal() promotes the dialog above everything, regardless of z-index or parent overflow. That chat widget with z-index 2147483647? The dialog renders above it. This alone has ended hours of stacking-context archaeology for me.

::backdrop is a real, styleable pseudo-element:

dialog::backdrop {
    background: rgb(2 6 23 / 0.6);
    backdrop-filter: blur(2px);
}

Escape works out of the box, firing a cancel event you can intercept if there are unsaved changes.

Light dismiss (click outside to close)

The one behavior not built in yet everywhere. The trick: clicks on the backdrop target the dialog element itself, clicks inside target children:

dialog.addEventListener('click', (e) => {
    if (e.target === dialog) dialog.close();
});

Give the dialog's inner content a wrapper with padding: 0 on the dialog itself, or the padding area counts as "the dialog" and closes unexpectedly.

Two honest caveats

  • show() (non-modal) doesn't trap focus or add a backdrop - most of the time you want showModal().
  • Animating open/close needs the newer allow-discrete / @starting-style CSS, which is Chromium and recent Safari/Firefox; older browsers just skip the animation, which is a fine fallback.

Between dialog for modals and accent-color for form controls, a surprising amount of "UI library" territory is now just the platform.

C
Written by Carlo Fontanos

Full-stack web developer sharing practical tutorials and building tools that ship.

Got something on your mind?

My inbox is open - no forms disappearing into the void here.

  • Just say hello Found a tutorial useful? Spotted a mistake? Tell me.
  • Hire me for a project Have something custom in mind? Let's talk scope and timelines.
  • Product support Bought something here? I'll help you get it running.

I usually reply within 1-2 business days.

Message sent!

Your details are only used to reply to you.

Keep reading