Previously On Locally Sourced: I wrote about how to use Hotwire and Turbo. (My Mac keeps wanting to autocorrect that to “Hot-wire” for some unknown reason…). The update to Modern Front-End Development With Rails is ongoing, the book is available for beta purchase if you want the new stuff as soon as we can get it to you.
After writing a post a couple of weeks ago explaining how the new Hotwire framework works I want to spend some time explaining what I like about it.
DHH makes some great points on this episode of Full Stack Radio podcast, and I want to pull a couple of them out because they resonate with what I find compelling about Hotwire and Turbo. I’m going to be expanding on what DHH says pretty freely here, so you should check out the podcast if you want to hear about Hotwire from the source.
One nice feature of Hotwire that DHH calls out is that it is very much a system of progressive and incremental enhancement. For this reason, I think it works best on sites where it’s easy to imagine the interactive client-side as an enhanced version of a traditional, page at a time, web app. It’s probably not going to work as well for you if you are building something that is wildly different from a traditional web paradigm, like a game, or, I don’t know, Google Sheets. DHH is upfront about the point that Hotwire isn’t for everything or for all problems.
What makes Hotwire useful in this context is that, for the parts of the app that don’t need client-side interactions, the cost of using Hotwire is just about zero. That may sound like a weird thing to boast about. But even a complex web application will have many, many pages where the needed amount of client-side fireworks is zero. For a lot of other pages the needed amount is, like, one thing. The number of pages that need a lot of complicated client-side work is often quite few, even on a complex site.
If you use Hotwire, you give make each page exactly as much interaction as it needs. Making one page complex does not cause complexity to transfer to other pages. (Especially if you are using Rails conventions overall, Hotwire mostly uses Rails you need to write anyway). If a page needs no interactivity, it works with Turbo Drive out of the box. For a small bit of interactivity, Turbo Frames lets you have it with no custom code. If you need more interactivity, Turbo Streams is there with a small amount of server-side restructuring, and then there is Stimulus when Turbo Streams fails. And even then Stimulus lets you do a lot with a little code.
David is emphatic on the podcast that you should pay the price for adding complexity to a page only when needed, and that it’s more than worth carrying a little extra overhead in HTML download to save developer work until you identify the cases that most benefit from the optimization work.
As a contrast here, React. Which I basically like as technology (it’s the larger ecosystem that I have problems with). But if you make your web server an API and build the entire site as a React single page app, you are taking on an enormous level of complexity across your entire application to support the potentially few pages that really need it.
In Hotwire, you aren’t taking on that complexity site wide. The enhancements are add-ones to the Rails app you’d already be writing, if you were writing a Rails app.
Quick personal anecdote, because everybody knows that anecdotes equal data, right?
My first serious attempt to build a single page app at Table XI was actually in Ember (React wasn’t around yet). And we floundered mightily and eventually realized that although Ember was a lovely tool, we didn’t actually need it to build the site. We got to the point a few months in where a teammate (who might well be reading this) volunteered to rebuild the whole thing in vanilla Rails in a weekend. I’m pretty sure he could have done it too, and I probably should have been more in favor of doing so at the time.
The other point DHH made that I’d like to call out is about consistency factions. I’ve don’t think I’ve heard him say it this clearly before, but he said that REST turned out to be one of the most important pieces of Rails. When Rails switched to it, what seemed like a constraint – limiting controller actions to a common set of actions – was actually imposing consistency and frequently cleared up complex code.
Once that consistency existed, Rails was able to use it to make writing basic Rails code even easier. And eventually, Rails got good enough at managing these consistent resources and actions that it became useful to organize code around as resources even where it’s not a completely natural fit.
With Hotwire, Rails now has a client side story that is almost as consistent as REST. You see this very clearly in Turbo Streams, where there are exactly five actions that you can perform on an incoming HTML response.
But really, the consistency applies to Hotwire as a whole in a slightly more diffuse way. Hotwire encourages you to see your site as a set of frames that can be replaced or augmented by HTML from the server. And again, if you start thinking of that as the organizing principle, there’s a lot of seemingly complex interactions that become much simpler to implement.
Even when you hit the limits of what you can do with rewriting HTML, Stimulus also has its own organizing principles that simplify common actions. Replacing CSS classes based on an action in Stimulus is very easy. There’s a huge number of things on a page that can be done by just changing CSS classes. Showing or hiding things. Animation. Sorting lists.
These are both things that I believe in generally in web development: that simple things should remain simple while complex things are possible, and that a consistent structure for thinking about the actions in your site can be valuable even if it appears to constrain your set of actions. I’m really excited to try Hotwire out for real (my previous work that used Stimulus pre-Turbo has been very successful). You should try it to see if it works for you.