Replace Turbo confirm with native dialog

This article was originally published on Rails Designer Blog

Rails, when using turbo(-links), has long shipped with a built-in confirmation dialog for destructive actions. You’ve probably used it countless times:

<%= button_to "Delete",
  post_path(post),
  method: :delete,
  data: { turbo_confirm: "Really delete this post?" }
%>

It works. It’s simple. But it’s also… well, a bit boring. The browser’s native confirm dialog is functional but not exactly pretty. And customizing it? Not really an option.

The default turbo_confirm uses the browser’s built-in confirmation dialog. It gets the job done, but you have no control over how it looks or what content it displays. Want to add formatting? Can’t do it. Want to match your app’s design? Nope. Want to include additional context or warnings? Not happening.

Though Turbo actually provides a way to override the confirmation UI, and if you’re using Rails Designer’s UI components, you even get a few styled options out of the box.

But today I want to explore another approach that gives you more flexibility. This is how it will look:

Preview of turboless confirm dialog

As always, the code can be found this repo

Let’s start by removing the turbo_confirm data attribute from your button. Change this:

<%= button_to "Delete",
  post_path(post),
  method: :delete,
  data: { turbo_confirm: "Really delete this post?" }
%>

To this:

<%%== tag.button "Delete", type: "button", data: { action: "dialog#openModal", target: "##{dom_id(post, :dialog)}"} %>

Notice it is now using tag.button instead of button_to. The data-action tells Attractive.js (more on this later) to open a dialog. The data-target points to which dialog to open using the post’s unique ID.

Right after your button, add the actual dialog:

<%= tag.dialog class: "dialog", id: dom_id(post, :dialog), closedby: "any" do %>
  

Really delete?

Are you sure you want to delete post "<%= tag.b post.name %>"? This cannot be undone.

class="buttons"> <%= button_to "Delete", post_path(post), method: :delete %>
<% end %>

The closedby="any" attribute means clicking outside the dialog or pressing Escape will close it. Inside, you have complete control over the content. Add formatting, include the post name or show warnings. Typically you want to extract this into a reusable component.

The actual delete button (that was replaced earlier) lives inside the dialog now. When clicked, it performs the real deletion. 🚮

To make the dialog open and close smoothly, add Attractive.js to your layout. In app/views/layouts/application.html.erb, add this line in your :


Check the docs for more installation details.

That’s it. No custom JavaScript to write. Attractive.js provides the dialog#openModal action that handles opening the dialog element. It’s a JS-free JS library (yes, really).

Then all that is left is style the dialog however you want. See the repo for a styling idea. Take especially note of the use of @starting-style (I explored it in this article on modern CSS).

When to use which approach?

So when should you stick with the unstyled turbo_confirm versus setting up a custom dialog?

Use turbo_confirm when you need something quick and simple. If you’re building an admin interface or internal tool where aesthetics don’t matter much, the browser’s default confirm dialog is perfectly fine. It’s one line of code and it works everywhere.

Switch to a custom dialog when you need more control over the content and presentation. Want to include the item name in the confirmation message? Show additional context or warnings? Match your app’s design system? Go for the custom dialog approach.

The setup is still simple (no custom JavaScript required!) but you get complete control over the experience. ✨

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post

Siemens Acquires Canopus AI to Bring AI-based Metrology to Semiconductor Manufacturing

Related Posts