· 5 min read

Hotwire and Turbolinks

This article was auto-translated from Chinese. Some nuances may be lost in translation.

Introduction

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.

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 load only fire on the initial page load because Turbolinks doesn’t reload the entire page. You need to listen to events like turbolinks:load instead.
  • 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

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

Explore Other Topics