How to Show a Delete Confirmation Dialog in Rails Using Stimulus

January 8, 2022   • hotwire

Update: Use this solution.

<%= link_to 'delete', post_path(post), data: { 'turbo-method': :delete, action: 'click->post#delete' }, class: 'ml-3 text-sm font-light' %>

If you’ve switched to the latest version of Rails that uses Turbo, you might have noticed that it doesn’t show the confirmation dialog when you delete an item. This post explains how to display a delete confirmation dialog using Stimulus.


If you haven’t worked with Stimulus before, it’s a JavaScript framework from Basecamp that is part of the Hotwire (HTML over the wire) front-end framework stack. The primary aim of Stimulus is to enhance the static HTML rendered by the server, using convention-over-configuration.

Stimulus connects the DOM elements to JavaScript objects using controllers and hooks the DOM events to JavaScript methods using actions. It does this using simple attributes on the HTML elements.


Controllers connect DOM elements to JavaScript objects using a data-controller attribute. The JavaScript object contains the behavior and logic you want to add to that DOM element.

Stimulus continuously monitors the page, waiting for HTML data-controller attributes to appear. Once it finds an element with a data-controller attribute, it checks the value to find a corresponding controller class. Then it creates a new instance of that class and connects it to the element.

For example, let’s assume your HTML contains this div element with the data-controller attribute.

<div data-controller="books">

Once Stimulus finds this DOM element, it will try to find a controller class in the books_controller.js file.

// src/controllers/books_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {

You can learn more about this process of mapping filenames to attributes on the Stimulus installation guide.


Actions connect DOM events to controller methods using a data-action attribute.

Let’s add a button to our DOM element with a data-controller attribute.

<div data-controller="books">
   <button data-action="click->books#read">Start Reading</button>

The data-action value click->books#read is called an action descriptor.

  • click is the event name
  • books is the controller name
  • read is the method to call

The data-action="click->books#read" tells Stimulus: when the user clicks this button, call the read method on the books_controller.

// src/controllers/books_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
    read() {
         // start reading the book

The Stimulus handbook compares this approach to HTML classes connecting to CSS styles.

Just like the class attribute is a bridge connecting HTML to CSS, Stimulus’s data-controller attribute is a bridge connecting HTML to JavaScript.

Data attributes help separate content from behavior in the same way CSS separates content from presentation.

With that basic introduction of Stimulus out of the way, let’s see how we will solve our problem of showing a confirmation dialog when the user clicks the delete button.

Here’s the example code that displays the books with a delete button for each book.

Step 1: Add a data-controller attribute to a parent HTML tag

Step 2: Add a data-action attribute to the button

Step 3: Add a Stimulus controller

That’s it. When you click the delete button, Stimulus will first call the delete method in the books_controller.js file. This method shows the confirmation prompt and does nothing if the user selects the cancel option.

And that’s how you can show the confirmation prompt using Stimulus in Rails.

Note: There’s a simple way to add the confirmation dialog using the turbo-method and turbo-confirm data attributes, like this:

<%= link_to "delete", book, data: { turbo_method: :delete, turbo_confirm: "Are you sure?" } %>

However, this solution runs into a problem where Rails tries to redirect you to the same page with an HTTP delete method, causing an error. Chris Oliver from GoRails recently did a video that explains this in detail, and also shows the workaround.

I hope that helped. Let me know in the comments if you find any mistakes or have any feedback.