Ludovic Frank - Freelance developer

Dialog, the native HTML element for modals and dialog boxes, supported everywhere since March 2022

ionicons-v5-k Ludovic Frank Sep 26, 2025
69 reads Level: intermediate

Hey folks 😁,

Thanks for clicking on this article, we're staying in the web world and I hope you'll enjoy it! 🙂

Today we're going to talk about a web API that I really like, and which is now well supported by browsers, the dialog element.

Are you ready? Let's go! 😉

Why this article?

As you probably already know, I'm currently working on Coupéo, the ViteUneTable but for hairdressers this time.

And my favorite front-end stack remains the same, Hotwire (Turbo and Stimulus).

The backend, on the other hand, has changed, it's Rails, but that doesn't really interest us for today.

Turbo confirm...

In the application sometimes, I need to ask for confirmation before an action, for example, when the user wants to delete something.

The thing is, by default, turbo uses an alert for confirmation...

And I've always hated alerts...

This isn't new, I've always found this method horrible, even back in 2012 I thought it was terrible...

In my old projects (for example ViteUneTable), I used SweetAlert2, which I loaded asynchronously, because I have to admit the alerts are pretty but that's a lot of Javascript and CSS to load...

The project being a Rails project and the community being huge, I thought "someone else must have had this problem... how did they solve it?"

After searching a bit I came across this code, perfectly integrated with Hotwire.

And concretely, when you look at this code, it replaces the Turbo function (the one that calls the alerts) with a function that uses the browser's Dialog API.

So I integrated it into the project (the screenshot above), and I decided to go much further with this API.

Why don't I like alert() at all?

The user experience is atrocious, it takes the user out of the application's "atmosphere", there's this broken interface that pops out of nowhere... From a UX point of view, it's a lazy solution that's really not good.

Moreover, it's blocking, when you call alert() you block all the code that comes after it, JavaScript being basically a language that's designed to be asynchronous (you can't block the user interface like that), I find that this function stands out in the JavaScript world.

The dialog element and API

Does it only replace alert()?

Absolutely not, in fact, for me that was my entry point, as explained above, but if you've worked with "bootstrap modals" then, you can replace them with this native API to make more complex interfaces than just a simple confirmation.

In fact, you can see that DaisyUI uses dialog(s).

Concretely how does it work?

First of all, on your page you'll need a <dialog> element, which is indeed a valid HTML element.

A small example?

That's the confirmation dialog I use with Turbo.

And the magic is that now with this element you have several available functions... in JavaScript.

Yes, yes these functions are directly exposed by the browser, no need for additional libraries...

We'll talk later about the fundamental differences between the available methods...

How I use Dialog with Hotwire (Turbo and Stimulus)?

First of all since a picture is worth a thousand words, here's a demo of what happens when I click on "new service" in the Coupéo management interface.

So, what's happening in this GIF?

In the "new service" button link, it's a GET that's why we see that before I click Turbo preloads the data from the server, I told turbo that the target of this button is the "modal" turbo-frame which is included in the layout of the management part of this application.

The server will respond something like this:

As you can see, it's HTML, the layout is simplified because Rails is configured to return an empty layout when the request contains a turbo frame identifier.

And in this layout, there's our turbo-frame with our dialog inside.

At this point, that's good we now have our dialog in the page, but it won't open...

Remember, to open it, we need some JavaScript.

If you're observant, you saw that at this moment, a Stimulus controller loads, this controller will do the work for us.

And here's the code for my Stimulus controller:

When Turbo injects our dialog into the DOM, Stimulus sees that it calls a controller, the "dialog" controller.

So it loads our controller and calls the "connect" method, the connect method takes care of loading the Javascript "showModal()" method for us, which has the effect of displaying the modal... simple as that.

Also, the Stimulus controller handles closing our modal, when we click cancel or when we cancel, right after that it cleans the turbo-frame.

Cleaning the turbo frame could be done with an HTTP request and Turbo, but that didn't make much sense... if I can save an HTTP request I might as well save it.

And there you go!

The difference between showModal() and show()

We talked about it earlier, there are two functions to open a dialog...

Here's a very telling example of showModal.

Here, we see that the intl-tel-input dropdown ends up behind the modal... Why?

Actually! when we use showModal, the browser creates the backdrop by itself, and puts the modal on top, regardless of the z-index, the modal isn't really "in the DOM" it's separate, on top...

That's why you'll never be able to put something "on top of your modal".

Conclusion

And there you have it, no more excuses for using alert() ;

See you soon for new adventures, I'm not sure exactly when, until then...

Take care 🙂.