Delete in Rails Without jQuery and UJS

As older versions of browsers die out, it's more and more common that we don't have a need for jQuery on certain Rails projects. So why can't we just remove these lines from application.js?

//= require jquery
//= require jquery_ujs

It's not abundantly clear, but a few select pieces of Rails are very reliant on jQuery and jQuery UJS. The particular features, which I'd recommend reading over before continuing, are documented in posts like A Tour of Rails' jQuery UJS and this comment from a jQuery UJS maintainer. Whether you're on a performance budget or just looking to remove unused code, this post will show one way to ditch jQuery and UJS and keep feature parity with two simple use-cases: delete links and confirmation dialogs.

link_to "Delete"

Deleting a resource is commonly handled with the link_to helper. Here's an example:

<%# views/posts/index.html.erb %>
<%= link_to "Delete", post_comment_path(@post, comment), method: :delete %>

This outputs an anchor tag that jQuery + UJS scripts into a form submission on the client side:

<a rel="nofollow" data-method="delete" href="/posts/1">Delete</a>

Unfortunately, this link is completely broken without jQuery and UJS, and it seems odd when you can use a regular form element in its place. Enter button_to:

<%# views/posts/index.html.erb %>
<%= button_to "Delete", post_comment_path(@post, comment), method: :delete %>
<!-- sample output for button_to -->
<form class="button_to" method="post" action="/bookings/46">
  <input type="hidden" name="_method" value="delete">
  <input type="hidden" name="authenticity_token" value="">
  <input type="submit" value="Delete">

It's definitely more markup, but it's the expected result. If you don't need UJS for anything else, you're already done!

Confirmation Dialogs

Another feature that the link and form helpers provide is support for confirm:

<%= button_to "Delete", post_comment_path(@post, comment), method: :delete,
              data: { confirm: "Are you sure?" } %>

The helpers store the confirm text in the data-confirm attribute, so it's easy enough to support this with just a small snippet:

class Confirm {
  constructor(el) {
    this.message = el.getAttribute('data-confirm')
    if (this.message) {
      el.form.addEventListener('submit', this.confirm.bind(this))
    } else {
      console && console.warn('No value specified in `data-confirm`', el)

  confirm(e) {
    if (!window.confirm(this.message)) {

Array.from(document.querySelectorAll('[data-confirm]')).forEach((el) => {
  new Confirm(el)

Delete and Confirm

Putting it all together, you may also want to abstract this new delete into a helper, like Eli did for a recent project:

# app/helpers/delete_link_helper.rb
module DeleteLinkHelper

  def delete_button_to(title, url, options = {})
    html_options = {
      class: "delete_button_to",
      method: "delete"
    }.merge(options.delete(:html_options) || {})

    form_for :delete, url: url, html: html_options do |f|
      f.submit title, options
<%# views/posts/index.html.erb %>
<%= delete_button_to "Delete", post_comment_path(@post, comment), {
  data: { confirm: "Are you sure?" }
} %>

And that's it! Without a lot of work, we dropped over 100 kilobytes (minified, not gzipped) of unnecessary JavaScript from page loads and learned a thing or two along the way.

Update: if you're looking for a complete drop-in solution without jQuery, try the vanilla-ujs gem from Łukasz Niemier.

Chris is a front-end developer who's passionate about web performance. He works in our Durham, NC, office for clients such as ESPN, Dick's Sporting Goods, and the Wildlife Conservation Society.

More posts by Chris