Heading image for post: The Rails Renaissance

Ruby

The Rails Renaissance

Profile picture of Jack Rosa Profile picture of Matt Polito

The power of Rails with Turbo and Stimulus.

With the imminent release of Rails 8 and the exciting new version of Hotwire's Turbo library, the buzz around Rails has been growing. Hotwire, which stands for 'HTML Over The Wire', encompasses the Turbo, Stimulus, and Strada libraries. Rails has integrated Turbo and Stimulus into the framework, providing a wealth of modern, high-speed functionality. Let's explore how these new additions, combined with some classic features, can make Rails the perfect choice for your next project, saving you time and boosting your productivity.

Turbo 8

Turbo has been around for a while now, but with Turbo 8, responsive page loading has never been more straightforward. Without writing JavaScript, your views can rapidly replace DOM content with minimal configuration.

With Turbo 8 and Rails, you can set up your views to subscribe to change events published from their corresponding models. This allows for the new content to be rapidly swapped out with concise diffs.

With older versions of Turbo, more specificity was required when configuring buttons, links, or frames to designate which view elements would need to be updated when their dependent data had changed. Turbo 8 introduces some changes that can do some of this for you.

Including this simple line in your model allows Turbo configured views to update automatically via ActionCable pubsub messages.

# in the model
class Blog < ApplicationRecord
    broadcasts_refreshes
end
# in the view
<%= turbo_stream_from @blog %>

Now, anytime a blog record is created, updated, or destroyed, that view will update accordingly and automatically reload the necessary content without the user refreshing 🤯. Turbo is cleverly "diffing" the content, only swapping the state that has changed.

To improve the responsiveness of these refreshes, we also have the option to preserve the user's scroll position on the page without writing any Javascript.

<head>
<%= turbo_refreshes_with method: :morph, scroll: :preserve  %>
</head>

Also, Turbo makes it easy to flag elements with data-turbo-permanent if you want the element ignored by the morph "diffing"

Streams

Turbo Streams have been around for a while, offering a presentable solution for building infinite scrolls, dynamic pages, and live websocket connection features in your app. With just a bit of setup in your controller, model, and views, you can create a live responsive chatroom in Rails without having to write any JavaScript.

To demonstrate these features, I've put together a barebones Rails chatroom app where multiple clients can connect to the same chatroom and send and receive messages in real-time. Check out the rails live chat repo and test it for yourself. You will see how simple it is to get the ball rolling with responsive features through Turbo Streams.

If you look at the code, you will see that working with Turbo Streams and WebSockets can bring in some interesting gotchas that you don't usually have to think about in Rails. Take, for instance, the messaging form partial:

<%= form_with(model: [@room, @message], data: { turbo_stream: true }) do |form| %>
  <%= render "messages/textarea" %>
  <%= form.submit "Send", class: "w-full rounded-lg bg-slate-500 text-white"%>
<% end %>

Notice on line 2 that I'm rendering another partial within the form specifically for the content text area. Here's what's in that "textarea" partial:

<textarea name="message[content]" id="message_content" class="mb-2" placeholder="your message..."></textarea>

Why would I do this? Well, the reason is that when we submit a form with a Turbo Stream response, the textarea input does not clear because we are no longer redirecting in our controller and reloading the DOM like you normally would with Rails. To get around this problem, in the Turbo Stream response sent by the messages_controller on the form submission, we specifically target the textarea input to replace and reset it.

This is the view for views/messages/create.turbo_stream.erb
<%= turbo_stream.replace "message_content" do %>
  <%= render "messages/textarea" %>
<% end %>

Of course, if this form had more fields, you could just target it and swap it out in the same way. Anyway, this is just an example of some of the things that need to be considered when using Turbo Streams.

Stimulus

For the extra touches of your front-end behavior that Turbo does not handle, Rails now includes the Stimulus JS library for sprinkling in bits of functionality into your views; this design encourages writing compartmentalized JS to keep your code from running off the Rails.

Using Stimulus is as simple as creating a controller and hooking your view up to it. Here's a solid example from the stimulus site

First, some HTML:

<div data-controller="hello">
  <input data-hello-target="name" type="text">

  <button data-action="click->hello#greet">
    Greet
  </button>

  <span data-hello-target="output">
  </span>
</div>

And then a simple controller:

// hello_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "name", "output" ]

  greet() {
    this.outputTarget.textContent =
      `Hello, ${this.nameTarget.value}!`
  }
}

The controller reads the target values out of the HTML. Notice how the button tells the controller which DOM event should trigger the controller greet function. On the data-action attribute, click is the DOM Event, hello is the controller name to connect to, and greet is the function to call on the controller.

With this simple setup, there are a lot of handy use cases, including:

  • Custom input behavior (scrolling, clicks, keyboard events)
  • Form submission and validation behavior
  • Dynamic search feedback
  • Managing DOM element states

Stimulus is intended to do all of these features without commandeering your entire front-end. Instead, you can implement these small controllers to add bits of interactivity where needed.

Convention + Responsiveness

While some developers have strayed away from using Rails front-ends over the years in favor of other frameworks such as Vue.js or React, Rails is still the king when it comes to prioritizing convention over configuration. Ruby on Rails saves valuable development time with its standardized design patterns. Now, with Turbo 8, many of the responsiveness features that developers have looked elsewhere for can be found and implemented quickly and easily in Rails.

It's also relatively straightforward to refactor existing Rails apps that don't use Turbo to modernize them with Turbo.

Why Rails?

  • Convention prioritized design
  • Maintainable Architecture
  • Responsive Front-End Features
  • Elegant Ruby syntax

Here & Now

Rails feels just as modern as ever. I encourage anyone to spin up a simple Rails project and try some of this stuff out.

More posts about Ruby rails Hotwire Turbo Stimulus

  • Adobe logo
  • Barnes and noble logo
  • Aetna logo
  • Vanderbilt university logo
  • Ericsson logo

We're proud to have launched hundreds of products for clients such as LensRentals.com, Engine Yard, Verisign, ParkWhiz, and Regions Bank, to name a few.

Let's talk about your project