Introducing Hotwire

One of the most visible and controversial new developments with the release of Rails 6.0 is Hotwire. Like anything in web development, the growing popularity of single page applications using JS frameworks has sparked some resistance. Arguably, the duplication of logic that single page applications often require violates the DRY principle. There are also many developers who would like the responsiveness of single page applications without all of the client side JavaScript and it's associated headaches. There are a considerable number of developers who'd like the pendulum to swing back towards the server, but maybe keep some of the more desirable aspects of the SPA pattern. Hotwire is a fresh take on how to do this the Rails way.

What is it?

Rather than sending a large chunk of JavaScript as an asset and then updating pages by requesting JSON fragments, Hotwire falls back to the more direct method of simply updating pages directly with HTML. This means that all your updates can be rendered directly using templates and partials, just like you're already used to. That keeps rendering on the server, where many would argue that it belongs. And because you're not sending a large slug of JavaScript, the initial page will generally load faster.

If you're like me, you're already asking yourself, "isn't that just AJAX by another name?" While it definitely shares the same basic concept of updating the DOM in situ, there's a couple of important differences.

Streaming updates

For starters, where AJAX is a hack to get around some limitations imposed by the HTTP request lifecycle, Hotwire does away with discrete requests entirely through the use of Websockets. Although Websocket is compatible with HTTP, it's treated as a fundamentally different protocol. Naturally, Rails provides a handy framework component in the form of ActionCable to help with setting up and managing Websocket connections. Hotwire is built on top of ActionCable. Rather than conventional HTTP requests, link navigation and form submission events in a Hotwire application will be sent over a Websocket connection, and the generated responses will be returned over the same connection. Unlike HTTP, Websocket is a full duplex protocol, so these request / response cycles can happen fast. Because the connection is persistent, updates can be made even faster still without the overhead of repeated connection / teardown cycles.

Broadcasting and pushing

Another important distinction between Hotwire and AJAX is the ability to push updates, and to do so to multiple clients at a time. Previously, in order to "push" updates to an application, developers would have to use a work around in the form of long polling. Instead of truly pushing updates to the client, the client would be responsible for opening and maintaining connections. With a true, persistent connection, this is no longer necessary. And since every client is managing it's connection via ActionCable, it gains all of the capabilities that ActionCable provides - namely, the ability to subscribe to channels. This is a big deal because this now means that all of the infrastructure for pub-sub based applications is available out of the box. There's no more need to roll your own complicated, one-off solutions for pushing messages to clients or even groups of clients.

Turbo

While this idea of streaming HTML updates over a Websocket sounds great, you're still going to need to write JavaScript to make it all happen, right? That's where Turbo comes in. Written in TypeScript, Turbo encapsulates all of the logic you'd be writing yourself to manage DOM events and updates. Although it's split into a number of smaller sub-libraries, each is a fairly small and more or less single purpose library - arguably a good example of well factored code. There's a library to intercept click events and form submissions (Turbo Drive), a library to help split pages into independently managed segments (Turbo Frames), and a library to introduce a new streaming DOM element (appropriately enough, Turbo Streams). Since Turbo already handles all of the update and replace related logic, there's almost no Javascript left for you to have to worry about.

Stimulus

Of course, let's be realistic here - there will be times when you need, or just really really want, to write some Javascript to handle something on the client side. The final component of Hotwire is aimed squarely at this need - Stimulus. In keeping with the goal of writing as little Javascript as possible, Stimulus is described as "a JavaScript framework with modest ambitions." Unlike more heavy weight frameworks that aim to create a full featured application on the client, Stimulus in many ways reminds me more of jQuery. It's mostly concerned with attaching behaviors to elements and responding to basic events. It brings a more modern flavor to the table though, by allowing you to attach classes that implement behaviors to objects in the DOM.

Stay Tuned

All of this is still technically beta software, so temper any excitement with some caution. That said, this seems to be the approach that the Rails team has committed to, so it's reasonable to expect a production release real soon now.

Since Rails is the platform that all of this is debuting on, it's natural to assume that Hotwire is tied to Rails. As it turns out, though, it's not - Hotwire is explicitly designed so that it can be used with any backend. The Rails implementation is simply a "reference implementation." Why should Rails devs have all the fun?

With the release of Hotwire, in many ways, we've come full circle with the development model for interactive web applications. Hotwire is basically a continuation of techniques like RJS that has brought us all the way back to the days of AJAX. This time, though, it's a well factored component instead of an ad-hoc technique, and the addition of streaming updates is one of those "why didn't I think of that" ideas. In my next article, we'll take a look at a simple example application that uses all of these components.