Hotwire and Turbolinks
Introduction
Hotwire aka NEW MAGIC is finally here: An alternative approach to building modern web applications without using much JavaScript by sending HTML instead of JSON over the wire. This includes our brand-new Turbo framework and pairs with Stimulus 2.0 😍🎉🥂 https://t.co/Pa4EG8Av5E
— DHH (@dhh) December 22, 2020
DHH (creator of Ruby on Rails) posted a tweet introducing his new project, Hotwire. DHH is notoriously critical of SPAs (Single Page Applications)—as hinted in his tweet—and actively avoids introducing excessive JavaScript into web development. This tweet sparked a lively discussion on Twitter, which I’ll briefly summarize here.
Here is the official description of Hotwire:
Hotwire is an alternative approach to building modern web applications without using much JavaScript by sending HTML instead of JSON over the wire
There are two key takeaways from this description:
- Minimal JavaScript required
- Sending HTML directly instead of JSON over the wire
This concept isn’t entirely new; years ago, Ruby on Rails adopted a similar approach called Turbolinks.
What is Turbolinks?
Turbolinks is a JavaScript package typically paired with Ruby on Rails (though it can also be used as a standalone library). It primarily fetches HTML and swaps it directly into the page to avoid the cost of full page reloads and re-requesting CSS. Claiming that it “doesn’t require JavaScript” isn’t entirely true—JavaScript is still there, handled by the library, so you just don’t have to write any yourself during development.
For example, suppose you have a tag like this on the page:
<a href="/articles/1" data-remote="true">link</a>
If Ruby on Rails has Turbolinks enabled, clicking the link won’t trigger a standard page reload. Instead, Turbolinks does something like this behind the scenes:
fetch('/index.html').then(res => res.html())
.then((html) => $page.html(html))
This way, when a user clicks a link, the entire page isn’t reloaded from scratch. Instead, it fetches the HTML file for that page (via Ajax) and renders it directly with JavaScript. Ruby on Rails is tightly integrated with Turbolinks, so at times you barely notice Turbolinks is even there—you just think, “Wow, page transitions feel so much faster!”
The larger the HTML payload, the less noticeable this benefit becomes. But as long as the HTML itself isn’t too large, it delivers a significantly better user experience.
Why Does This Approach Work?
- Better user experience since pages don’t need to re-fetch CSS and JavaScript upon navigation
- Turbolinks handles managing elements in the head
- Requires almost no extra JavaScript to be written
- Backend engineers can achieve a much better user experience with minimal effort
Caveats
- Events like
loadonly fire on the initial page load because Turbolinks doesn’t reload the entire page. You need to listen to events liketurbolinks:loadinstead. - As JavaScript interactions increase, event listeners can easily conflict with one another.
- State is preserved across page navigations, so poorly written JavaScript can easily cause memory leaks.
As interactive JavaScript grows in complexity, pairing it with Turbolinks can sometimes lead to bizarre bugs, such as errors caused by repeated script execution.
Thoughts
As developers, most of us have built SPAs at some point and know firsthand that building a usable, well-crafted SPA is tough. Poorly managed state eats up memory, state gets out of sync, error handling becomes messy, and you constantly burden users with massive JavaScript bundles. Sometimes it’s simply cleaner to stick with pure server-side rendering (SSR) and enhance the experience with something like Turbolinks—which might actually be a great alternative.
Afterword
It’s fucking hilarious that the tagline of this is “Hotwire is an alternative approach [...] by sending HTML instead of JSON over the wire”
— Surma (@DasSurma) December 22, 2020
HTML INSTEAD OF JSON? BLOODY MADNESS!
Then again, if this makes “sending HTML over the wire” popular, I’ll take it. https://t.co/7bLGdtJn7u
A lot of folks think serving JSON is more efficient than HTML, but once gzipped they're often the same. Sometimes HTML is smaller.
— Jake Archibald (@jaffathecake) November 15, 2017
Eg https://t.co/hgxyH0N3Ct:
JSON: 18k.
Generated HTML: 17k.
Also, it's much easier to stream HTML & get a progressive render.
Some believe JSON is smaller and therefore more efficient to transfer than HTML, but once gzipped, the difference is often negligible—and HTML can sometimes even be smaller. So rendering HTML directly over the wire isn’t inherently bad at all.
That said, Ruby on Rails remains the only framework with first-class, seamless Turbolinks integration. Hotwire is still relatively new, and many developers are likely taking a wait-and-see approach.
Related Posts
- Recreating My Room with Three.js Using React Three Fiber, I brought my real room into the browser—turning physical objects into an interactive table of contents, and using spatial memory to tell the story of my life and work over the past few years.
- Things to Keep in Mind When Using Images in Frontend Development Expanding on Jake Archibald's article, this post organizes how modern responsive images should be written: why width/height are still necessary, when to use CSS aspect-ratio, how to choose between AVIF and WebP, and using picture/source/srcset for art direction on mobile devices.
- CSS field-sizing — Auto-resize Form Elements with a Single Line of CSS Previously, auto-resizing a textarea required listening to scrollHeight in JavaScript. With CSS field-sizing: content, a single line replaces it all, supporting textarea, input, and select. This article covers the pain points of older approaches and how to use field-sizing.
- Make Your Link Underlines Look Better: text-underline-offset By default, underlines sit very close to the text. Some designers dislike this look, and personally, I don't think it looks great either.