Hotwire isn't only for Rails
Lewis Sparlin
Posted on December 15, 2021
At the end of 2020 the Basecamp team released a collection of Javascript libraries called Hotwire. Modern web stacks have popularized javascript-rendered front ends and JSON transmissions. Hotwire's primary motivation is to reduce the Javascript footprint and allow application front ends to be created in primarily HTML. It pairs very nicely with the Ruby on Rails ideology and is often demonstrated in that context. I aim to write a series on how Hotwire can be used in any application to simplify development and reduce the need for heavy Javascript downloads. Hotwire currently consists of two javascript libraries: Turbo and Stimulus. The first part of this series introduces Turbo.
HTML over the wire
Hotwire prioritizes HTML over other means of data delivery. Pages can be written in HTML without the overhead of javascript-rendered templates. Pages can be updated by sending HTML from the server (or just plain HTML files) instead of JSON or XML - which involves interpreting data with Javascript. Everything on the web ultimately presents itself as HTML, so why not start there?
What is Turbo?
According to their splash page Turbo is a javascript library that offers developers "The speed of a single-page web applications without having to write any JavaScript". This is pretty cool and the complete opposite of most popular Javascript libraries. Turbo doesn't render HTML from a template, it simply interprets an HTML page and adds common functionality. There is no Javascript interface; any desired functionality is added using plain HTML. data-turbo-*
attributes are sometimes used to declare non-default behavior.
The turbo library has three main concepts it calls Drive, Frames and Streams.
Turbo Drive
Turbo Drive is nearly invisible. A developer doesn't directly interact with it in most cases. Adding the Turbo library on any HTML page automatically translates all <a>
(link) and <form>
tags to Javascript interactions. Visiting a page in the application will quickly replace the page <body>
without the browser actually loading a new page. Submitting a form will have the same effect. This gives plain HTML applications a more interactive feel, similar to single-page applications, but without the heavier javascript frameworks. I'd love to give some code examples demonstrating Turbo Drive, but it really is that easy: import it, it works.
import * as Turbo from "@hotwired/turbo";
If you're not bundling javascript, Turbo can be included from a CDN like Skypack or Unpkg
<!-- from a CDN -->
<script type="module" defer>
import Turbo from 'https://cdn.skypack.dev/@hotwired/turbo';
</script>
Turbo Frames
Turbo Frames (and Streams) is the exciting part of the Turbo library. So much behavior can be added to an app with Frames - still just by writing HTML! I believe there is too much to cover in this Hotwire overview, the next part in this series will be dedicated to Frames. Here is a quick preview of what Turbo Frames can do.
Coffee Boutique
In an alternate reality I run a small coffee stand for people willing to wait for slow, manual coffee brewing methods. I offer pour-over, French press, and Aeropress coffees. The HTML for the self-serve brew method selection looks like this (simplified):
<h1>How shall I brew your ☕️</h1>
<turbo-frame id="coffee_selection">
<div>
<h3>Pour-over</h3>
<p>... describes pour-over ...</p>
<a href="choose_pour_over.html">
Pour my coffee
</a>
</div>
<div>
<h3>French press</h3>
<p>... describes french press ...</p>
<a href="choose_french_press.html">
(French) Press my coffee
</a>
</div>
<div>
<h3>Aeropress</h3>
<p>... describes aeropress ...</p>
<a href="choose_aeropress.html">
(Aero)Press my coffee
</a>
</div>
</turbo-frame>
Wrapping the selection components in a <turbo-frame>
causes links inside the tag to only effect the context of that frame. Clicking links or submitting forms will not update the entire page, instead they expect HTML from the response to contain directions on how to update the frame. When the response has a matching turbo-frame
ID, the contents in the frame are replaced with the contents from the respective tag in the response.
<!-- choose_pour_over.html -->
<turbo-frame id="coffee_selection">
<div>
<h3>Pour-over</h3>
<p>Great choice!</p>
<p>Wait time 5 minutes</p>
</div>
</turbo-frame
No Javascript was written to cause the component on the page to be replaced with user feedback. The <turbo-frame>
tag did all the work. Clicking the "Pour my Coffee" link causes the contents of the file choose_pour_over.html
to be fetched and Turbo replaces the frame.
My working code example is on Replit
Turbo Streams
Turbo Streams adds HTML tags that can give more granular page update directions. The <turbo-stream>
tag supports HTML attributes that give instructions to replace certain parts of a page or to append or prepend HTML from inside the tag. It's a super powerful part of the Turbo library and deserves its own coverage. I plan on including a Turbo Stream article later in this series to demonstrate the huge potential of communicating front-end update instructions using plain HTML.
A similar replacement like the example above would be more explicit with Turbo Stream markup.
<turbo-stream action="replace" target="coffee_selection">
<template>
<div>
<h3>Pour-over</h3>
<p>Great choice!</p>
<p>Wait time 5 minutes</p>
</div>
</template>
</turbo-stream>
Conclusion
Hotwire and specifically Turbo include a lot of useful tools for developing applications without a heavy Javascript framework. Turbo enhances page links and forms for free just by including the library. Frames and Streams are extremely powerful for building front end interactions. And all of it can be done in HTML. Hotwire seems most popular in the Rails community, but it has lots of potential on its own.
Links
- hotwired.dev homepage
- Turbo handbook
- Skypack CDN for node packages
- My Turbo Frames code example at Replit lsparlin/hotwired-frames-coffee
Posted on December 15, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.