The Pragmatic Studio

What Is Hotwire?

November 02, 2021

(If you prefer text over video, here’s the reader-friendly transcript…)

So what is Hotwire, and what problems does it solve?

To answer that, say we have a typical Rails app, and on this page there’s an interactive element. It sends an HTTP request to a controller action, and a new page of HTML is sent back as the response. Then the whole page is reloaded, even if only part of the page changed. And we know we could do better!

So before Hotwire, there was a grab-bag of options for making Rails apps feel more responsive.

For starters, we could have used Turbolinks. With Turbolinks, all link clicks are automatically intercepted. And instead of sending regular GET requests, it sends asynchronous JavaScript requests to fetch pages. Turbolinks then automatically merges the HTML document head and replaces the body.

So there’s no full page reload, which means page navigation is faster with Turbolinks. But it’s still replacing the entire body of the page.

Rails AJAX Helpers

What if we want to dynamically update only parts of a page?

Well, to do that we could have used Rails AJAX helpers and marked interactive elements as being remote. Instead of a link click sending a normal GET request or a form submission sending a normal POST request, these interactions send AJAX requests. And the Rails app generates JavaScript code that’s executed by the browser to dynamically update affected parts of the page.

JavaScript Components

Taking it a step further, we might have whipped up a React, Vue, or similar JavaScript component to make part of a page feel more responsive. The component sends an AJAX request to fetch data. The Rails app sends a hunk of JSON back as the response. Then the component transforms that JSON to DOM elements, and the DOM is dynamically updated to reflect any changes.

And who doesn’t like neatly isolated components? But this approach can be kinda clunky since the page is a mix of server-side rendering and client-side rendering.

And the more components we add, the clunkier it gets.

Single-Page App (SPA)

Another approach is to go all-in with client-side rendering by creating a so-called “single page app”.

In this scenario, the frontend is a separate application built with React, Vue, or another client-side JavaScript framework that sends AJAX requests. And the Rails app is strictly a JSON API, so all the rendering happens client-side.

Now if you’ve been down this path, you know it’s not lined with roses. There’s a whole lot of inherent complexity when it comes to building and maintaining two separate apps that interchange data. First off, you’re always context switching between JavaScript and Ruby. And you know when that happens it’s all too easy to end up with duplicate logic. Plus it’s on your shoulders to design an API that allows the frontend to efficiently fetch exactly the data it needs: no more, no less.

On top of that you have the challenge of keeping state in sync across the two applications. Oh, and don’t forget about all the additional tooling needed to bundle and deploy two separate apps. OK, so there are cases where building an SPA is warranted, but they should be the exception, not the rule.

What if there was a way to get the speed and responsiveness of an SPA without the complexity of an SPA, and without writing custom JavaScript?

Well, as you may have guessed, Hotwire gives us a practical alternative! It lets us return to the classic approach of rendering all the HTML server-side using Rails templates while keeping our app fast and reactive.

Hotwire

Hotwire is made up of Turbo and Stimulus.

Turbo is a set of technologies for dynamically updating parts of a page without writing any custom JavaScript. It includes:

  • Turbo Drive
  • Turbo Frames
  • and Turbo Streams

And for those situations where you need a dash of JavaScript, Stimulus is a lightweight JavaScript framework that nicely complements Turbo.

Let’s glance at each of these in turn…

Turbo Drive

Remember how Turbolinks intercepts all link clicks, and instead of sending regular GET requests it sends AJAX requests? Well, TurboDrive does that and also does the same thing for form submissions. In both cases, page navigation is faster because TurboDrive automatically replaces the current page’s body and merges the head.

No full page reloads!

Turbo Frames

Now replacing the body alone may make some pages of your app fast enough. But you might want to accelerate other pages even more by updating only part of the body.

That’s where Turbo Frames comes in.

Let’s say you have a link or form here. You would make this part of the page a Turbo Frame by putting it in a turbo-frame tag with a unique id:

<turbo-frame id="7">
  HTML
</turbo-frame>

Now any interactions within this frame are scoped to the frame. Clicking the link or submitting the form fires off an AJAX request to a typical Rails controller action. And a Rails template then only needs to render HTML for that frame. Turbo then automatically replaces just that part (or frame) of the page.

No JavaScript required!

Turbo Streams

But what if you want to dynamically update multiple parts of a page?

That’s when you need Turbo Streams!

In response to a user interaction, you return HTML consisting of Turbo Stream elements. These are basically directions that Turbo follows to update affected parts of the page—replace this, update this, prepend this, and so on:

<turbo-stream action="replace" target="target_a">
    HTML
</turbo-stream>

<turbo-stream action="update" target="target_b">
    HTML
</turbo-stream>

<turbo-stream action="prepend" target="target_c">
    HTML
</turbo-stream>

But wait, there’s more to Turbo Streams!

Let’s say you want to stream partial page updates to multiple browser sessions. No problem. Turbo Streams uses ActionCable under the hood to deliver updates asynchronously over a web socket connection.

And again, we get all this without writing any JavaScript!

Stimulus

But what if you need a sprinkle of JavaScript? For example, you need a snazzy animation, a way to show/hide content, or you want to use a JS library such as a datepicker. Well, these sorts of user interactions need to change the DOM, but don’t require a round-trip to the server.

And that brings us to Stimulus.

You know how Rails has controllers and actions? Well, Stimulus gives you a way to organize client-side JavaScript code in a similar way. You write a Stimulus controller (it’s just a JS object) that defines actions which are just JS functions. And those actions can do pretty much anything JavaScripty.

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  action1() {
    // anything JavaScripty
  }

  action2() {
    // anything JavaScripty
  }
}

Then, using HTML attributes, you connect a controller action to an interactive element and the appropriate action runs in response to DOM events being triggered:

<div data-controller="my_controller">
  <button data-action="click->my_controller#action1">...</button>
  <button data-action="click->my_controller#action2">...</button>
</div>

So Stimulus gives us a structured, unobtrusive way to add in bits of JavaScript.

Summary

What we really like about Hotwire is that it, well, turbo-charges what’s already in Rails. Things like:

  • server-side rendering and routing
  • controllers and actions
  • templates and partials

Indeed, Hotwire isn’t an all new way of building Rails apps. It’s about taking your traditional Rails app and incrementally improving it.

For some pages, Turbo Drive may give you all the boost you need. No application changes necessary; just drop it in and you’re off to the races! On pages that have some interactivity, you can strategically use Turbo Frames. Often times that’s as simple as taking an existing template, wrapping its HTML in a tag, and you’re done. And in scenarios where you need to broadcast partial page updates, you can layer in Turbo Streams with relatively minor code changes. Finally, when you need to reach for JavaScript, Stimulus is right at hand.

So that’s our take on Hotwire. Everything you know and love about Rails is still in play. Hotwire just brings new tricks to the game!

Want to see how to hotwire a Rails app step by step? 🏎

We'd love to show you in our Hotwire course for experienced Rails developers. Starting with a full-featured Rails app that could benefit from Hotwire, we incrementally improve each page step-by-step using Turbo Drive, Turbo Frames, and Turbo Streams where appropriate. This course has everything you need, assembled in the right order, and in one place! 👍

Hotwire Course