Confetti cloud - Credit: Hugo Ruiz

Software architecture

Hotwire: The utopia of ditching JavaScript

Introduction to backend-centric applications with Hotwire

This post is for:

  • Full-stack developers evaluating if they should invest in hotwire. Mid-senior devs can jump directly ot the Hotwire section.
  • Managers and business owners wanting to avoid overengineering costs. You can skim the post reading highlighted concepts, and concentrate on Pros and Cons.

Full read: 7 min

Introduction

Hotwire is a novel approach to web application development, aiming at drastically simplifying the client, without loosing the speed of modern websites.

It was introduced by the people at Basecamp and Hey.com, but it's not bound to a specific backend technology. At the time of writing, you can adopt it in:

and it has also been experimented with in Java

The goal of this post is to present general concepts, link resources with great examples and wrap up with my thoughts on pros and cons.

A new pattern

A modern webapp usually requires a lot of work in order to be in sync with the backend. If your use case is right, however, you can forget that hassle thanks to Hotwire: server-rendered views (or even single DOM elements) updated via websocket.

The paradigm changes:
In Single Page Apps, JavaScript was the engine.
In Hotwire, it can be either gone or just a User Interface utility.

Baseline: Single Page Applications

To understand Hotwire innovations, let's start looking briefly at the state of the art in frontend development: Single Page Applications. SPAs are web applications whose logic resides primarily on the client. As their name suggests, the browser only loads a SPA once. The application then manages the interaction flow asking the server just punctual information, usually data in JSON format, or assets for newly opened sections.

SPAs have many advantages over classic web applications, whose UX was slow and clunky. Remember? Every click was followed by a full page reload, you had a blank page flashing, and you had to wait for the next page for a few instants. Or several seconds, if the page was heavy. (E.g. pre-2010 online newspapers were a nightmare.)

Users appreciate SPAs because they allow for a smooth User Experience.

Developers appreciate SPAs because they organize the code in components, vastly improving reuse and maintaniability as far as the font-end is concerned.

Nonetheless, developing a Single Page Application has a few drawbacks:
  • Tooling
  • Duplication
  • Fast obsolescence

Tooling a SPA requires a lot of choices to be made: framework (React, Vue, Angular?), support libraries, data architecture. Frameworks now provide guidance on application structure, and very useful 'starter apps' that are already configured for basic scenarios. Unfortunately, if you want to customize something you still have to deal with dozens of config parameters, spread cross multiple files at different stages of the toolchain.
Hey, I sound critic but do love SPAs! This site has been are created with Nuxt, which means Vue.js + extra sugar.

Duplication is what threatens SPA maintainability the most: you can transfer most of your logic on the client, but you can't move it all:

  • form validation on the client side is neat, but the server cannot trust the client, it must validate the data on its own too;
  • if you have a model of some data on the client (customer profile, payment processing step...) you have to sync this model with the one on the server.

You're binding the core of your logic to a contract: the API. And when that logic changes you have to change it on both ends, and ensure the synchronization still holds.

Fast obsolescence is the curse of JavaScript. In other (backend) languages you can modernize a webapp that is 10 years old and save a lot of money over a full rewrite. In JavaScript you're probably breaking even much sooner. I would guess this time lies between 3 and 5 years.
Good luck finding someone accepting to maintain a 5yo webapp written in JS! Everything has changed in the ecosystem since the first commit.

Hotwire

Hotwire promises the best of both worlds:

  • simplicity, as in legacy webapps,
  • smoothness and usability, as in SPAs.

The name means "HTML over the wire", meaning that the client does not ask the server for data in order to render it. It receives HTML and simply replaces the corresponding DOM element(s).

Client and server trade HTML instead of JSON

One may wonder: "What about all the other actions that the client performs beside rendering?"

  • Business logic is certainly manageable on the server, since the old times. It belongs there (see 'duplication' above) and it's good news that it can go home.
  • Animations and all transient effects that do not need to leave a trace on the database can still be managed by the client.

I would say that we don't have to remove JavaScript completely, just to bring its scope back to the origins.

So, how does Hotwire work?

Tools. Credit: Jo Szczepanska
Photo: Jo Szczepanska

A Hotwire app is powered by a few libraries, each providing improvements to the user and the developer experience. You don't need to master them all to start. They have nicely isolated capabilities and you can mix and match them without worries.

Turbo Drive

Turbo drive accelerates website navigation by intercepting link clicks and form submissions. It caches assets and navigation state. You just need to know it's there and you get all the benefits for free. You may never interact with it unless you want to fine-tune its optimizations.
It's the sucessor of Rail's Turbolinks library, that I personally found confusing. I must admit, the improved benefits and the quality of the documentation made me buy Turbo Drive completely.

Turbo Frames

Turbo Frames allow for partial updates of an HTML page.
Hotwire defines the <turbo-frame> tag, that represents a DOM element to be updated, and the front- and back-end logic to modify the DOM using the frame. Only a few operations are allowed (replace, append, prepend...) by design. Advanced manipulation can be added with JavaScript/CSS, of course. Turbo Frames are great at augmenting plain HTML links and forms, without overwriting or removing any attribute. This way, the website still works without JavaScript and is progressively enhanced according to browser capabilities.

Turbo Stream

Turbo stream leverages web-sockets to provide live updates to connected clients.
It opens pub-sub channels that views subscribe to. Models publish their updates on channels, and the views receive updated content to show. If used in conjunction with Turbo Frames, it streams turbo-frames, otherwise it can provide DOM elements to be replaced based on their id.

Stimulus

Stimulus is a JS library to manage DOM elements reactively.
It allows to make that "last mile" to the ideal UI, adding JavaScript functions that control portions of the HTML page in response to events generated by the user or by Turbo.

After this overview, let me link a few examples. I will update the list when I discover new useful material.

Resources & examples

As I noted in the introduction, Hotwire is implemented in many languages.
You can read the language-agnostic Handbook and Reference for details.

Nonetheless working code has to be written in a specific language. I chose to link examples in Ruby on Rails for a number of reasons (e.g. it's visually clean, it's where the approach was born, and it was faster for me to try), but I hope they will be useful and easy to replicate in your language of choice.
If you prefer, you may jump to my conclusions.

Be advised that hotwired.dev hosts its own technical community forum, so consider going there beside StackOverflow.

Turbo Frames example

  • This Hotrails tutorial gives a very clear explanation via wireframes, alternated with updates to the actual code until the fulfilment of the requirements.

Live updating examples with Turbo Stream

  • A very nice post on the basics and how to control where to broadcast updates and to scope channels.

  • For a basic demo, you can watch from minute 23:24 of DHH's video introduction to Rails 7.

Stimulus examples

  • The Stimulus handbook on howired.dev is clear and complete with building progressively more complex controllers.

  • A nice first exercise would be to implement a Stimulus controller that applies SlimSelect (even after DOM rewrites) on a target multiple select field to manage a many-to-many association. After you've thought about how this habtmController might look like, you can look at the stimulus-slimselect package for further inspiration.
    Would it be useful to see an implementation here? In case, get in touch and let me know

Pros and cons

TL;DR: After trying by myself I overcame my strong diffidence and I recommend Hotwire adoption in the majority of cases.
The learning curve is not steep, and you can introduce every tool in the box independently, before you look into the other ones.

Icon For Star Icon For Star Icon For Star Centralizing the state on the server removes a lot of work.

Icon For Star Icon For Star Icon For Star Live updates out of the box really deserve the New Magic naming that was used before lauch. Websockets are great, even more when you don't have to manage them.

Icon For Star The vast majority of use cases I encountered in my career is covered by Hotwire features. You don't really need a Single Page Application in many webapps . There are exceptions: you may need to allow offline work, or to delegate lots of computation to the client. In all other cases a SPA may not be worth the effort.

Icon For Half-star Turbo instructions on what to do with an incoming frame are powerful but may lead to anti-patterns. A partial view that knows where it will be placed is the opposite of a contest-agnostic reusable element. We need discipline in organizing update logic, if we want to avoid an explosion of very specific (single-purpose) partials.

Icon For Half-star Some effort is required to design your first webapp around the new paradigm. While new powerful ways of doing things emerge, common patterns in SPAs are no longer as immediate to realize. E.g.: creating a resource via a form with a dynamic number of nested fields was trivial in SPAs, but in Hotwire it requires to ponder what this entails for the server - you'll ha ve to discern what to realize with Stimulus and what to realize with Turbo.

Icon For Highvoltage Turbo documentation is not as complete and accessible as I expected. This post comes from my need of gathering resources to learn Hotwire. I'm still unsure how to get an advanced understanding of Turbo Streams broadcasts without looking at the code. (I'll keep updating the post as I find out more)