Update: I originally published this post few months ago, and it only covered Turbo Drive and Turbo Frames then, with a static site.
I’ve since had a bunch of conversations with people working with other tech stacks (Rust, PHP, and Go) wanting to integrate Hotwire into their front-ends, and everyone kept asking about Turbo Streams, since it needs a back-end server. So I’ve updated the post to build a simple Sinatra app that uses Turbo Streams.
So far on this blog, we’ve learned about Hotwire and built a hotwired to-do list powered by Rails. We’ve also seen how you can iteratively build and progressively enhance an application using Hotwire. All these projects used Hotwire with Ruby on Rails.
However, you don’t really have to use Rails (or Ruby) to get most of the benefits of Hotwire. Most static websites can just drop-in the Turbo library to behave like responsive single-page applications, without incurring any of the costs and complexities associated with the SPA frameworks. And if you have an existing app written in PHP, Go, Rust, or even Java, you can start using Hotwire, right now.
All you have to do is follow certain conventions.
This article is divided into two parts:
- First one shows how you can use the first two Hotwire techniques (Turbo Drive and Turbo Frames) in a simple static website, to fetch and update entire web pages or parts of the page, without fully-reloading the browser. We’ll use a simple static site to demo this.
- Second part shows how to tweak your existing back-end code to send Turbo Streams to update multiple parts on your website dynamically, in response to form submissions. Since Turbo Streams work with form submissions, I’ll use Sinatra to demo this.
Here’re the topics we’ll cover in this article.
- How to set up a simple website
- Using an HTTP server to serve static pages
- How to install Turbo
- Faster navigation with Turbo Drive
- Dynamic page updates with Turbo Frames
- How Turbo Frames Work?
- Step One: Wrap Component in Turbo Frame
- Step Two: Wrap Response in a Turbo Frame with same ID
- Working with Turbo Streams
- Target Multiple Elements with Turbo Streams
- Step One: Use Turbo HTTP Header
- Step Two: Send Turbo Streams in Response
- Where to go from here
💡
For those of you who don’t enjoy reading long, rambling posts that try to explain every nook and cranny of the topic at hand, I’ve uploaded the finished project on my GitHub account, which also contains the instructions to get started. Just clone the repository, run npm run launch
, and you’re good to go.
Prerequisites: If you don’t know what Hotwire is, I suggest you check out the following article, written by yours truly. It briefly introduces Hotwire and explains the problems it solves.
A Brief Introduction to Hotwire
Hotwire, which stands for HTML Over the Wire, provides a different way to build modern web applications without using too much JavaScript. This article provides a quick introduction to Hotwire and it’s component frameworks, such as Turbo Drive, Frames, and Streams.
Akshay’s BlogAkshay Khot
Set up a Simple Website
In this section, we’ll set up a simple website that serves static files. Both Turbo Frames and Turbo Drive don’t need a backend server, so a simple static website should be simple and barebones enough to explain the basic concepts.
Create a brand new directory for your website and cd
into it. I’ll call mine wireframe
.
➜ mkdir wireframe
➜ cd wireframe
Run the npm init
command to set up a new project. It will ask you a bunch of questions and then create a package.json
in the current directory.
➜ wireframe npm init
package name: (wireframe)
version: (1.0.0)
description: Using Hotwire without Ruby on Rails
entry point: (index.js)
test command:
git repository:
keywords: hotwire, turbo
author: AK
license: (ISC) MIT
Here’s the resulting package.json
file
{
"name": "wireframe",
"version": "1.0.0",
"description": "Using Hotwire without Ruby on Rails",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"keywords": [
"hotwire",
"turbo"
],
"author": "AK",
"license": "MIT"
}
Now open the directory in your favorite editor. We’re ready to start coding.
➜ code .
Add a simple HTML file
Let’s create a new folder named public
with an index.html
HTML file in it. The HTML file will have the following content. Feel free to copy and paste.
Wireframe
Learn Hotwire
Yes, you can really use Hotwire without Rails. Give it a try!
To make our website look a bit nice, I am using SimpleCSS, a simple, classless CSS framework. It’s so simple that I don’t even have to explain it. Basically, it makes semantic HTML look good, that’s it. No classes required.
If you open the index.html
file directly in the browser, it should look like this. Pretty neat, right?

Notice that the nav bar says File
, instead of HTTP
, because chrome is directly serving the file. To make it look like a real website served with the HTTP protocol, we’ll need an HTTP server that will serve the HTML.
But why? If you’re curious to learn more about the difference between opening a HTML file directly in browser vs. serving it with an HTTP server, check out this StackOverflow question.
Using an HTTP Server to Serve Static Pages
I am going to use a simple static HTTP server called http-server
which is more than sufficient for our needs.
http-server
is a simple, zero-configuration command-line static HTTP server. It is powerful enough for production usage, but it’s simple and hackable enough to be used for testing, local development and learning.
Did you know that you can run the package without installing it first, using the npx
command?
Run the following command from the wireframe
directory.
➜ npx http-server
Starting up http-server, serving ./public
http-server version: 14.1.0
Available on:
http://127.0.0.1:8080
http://10.0.0.182:8080
Hit CTRL-C to stop the server
Without any arguments, the above command serves the index.html
file in the public
directory when you visit http://127.0.0.1:8080
or localhost:8080
. This is why I’d asked you to create a public/index.html
file for your project.
You can also add the above command as a script
in the package.json
file. This will allow you to launch the website using the npm run launch
command.
// package.json
"scripts": {
"launch": "npx http-server"
},
Now that your server is up and running, visit the http://127.0.0.1:8080
or http://localhost:8080
URL in the browser.

Now that our website is up and running, we’re ready to install Turbo.
How to Install Turbo
We are going to use the pre-compiled, optimized NPM package from skypack.dev using the tag, just like it’s 2007. For other installation methods, check out the Installing Turbo documentation.
Step 1: Add the following script
tag just above the
tag in your HTML.
Wireframe
Step 2: There’s no step 2. ;)
If you’re curious about how the above snippet works, I highly recommend you read the MDN documentation on JavaScript Modules.
That’s it. Our little website is using Turbo.
To verify, reload the browser, open the DevTools window, go to the Console
tab, and type Turbo
in it. If it doesn’t throw an error, you’re good to go.

Now that we’ve successfully installed Turbo, we’re ready to use it. We’ll start with the first big component in Turbo, called Turbo Drive.
Faster Navigation with Turbo Drive
It just works out-of-box.
The best thing about Turbo Drive is that you get it for free. Yes, you heard that right. You don’t have to do anything to get the benefits of Turbo Drive.
But how does it work?
When you click a link or submit a form (to the same domain), Turbo Drive does the following:
- Prevent the browser from following the link,
- Change the browser URL using the History API,
- Request the new page using a
fetch
request - Render the response HTML by replacing the current
element with the response and merging the
.
The JavaScript window
and document
objects as well as the element persist from one rendering to the next.
The same goes for an HTML form. Turbo Drive intercepts and converts Form submissions into fetch requests. Then it follows the redirect and renders the HTML response.
As a result, your browser doesn’t have to reload, and the website feels much faster and more responsive, just like a single-page application.
Let’s Add a Contact Page
To see how Turbo Drive works, we need to set up another page on our website that we’ll add a link to.
We’ve already added the links to the Contact and About pages when we wrote the initial HTML, so let’s go ahead and add a Contact page. To keep it really simple, I’ll just copy and paste the index.html
page, changing the filename and a little content to make it unique.
This is only for demo. Your back-end framework or static-site generator uses a templating system to extract all the duplicate HTML.
Wireframe - Contact
Contact
You can reach me at akshay.khot@hey.com
You should see the following page when you go to /contact
page.

Now go ahead and click back and forth between the Home and Contact links.
💡
Can you spot something different that you won’t see on a traditional website with a bunch of HTML pages linking to each other?
No? let me give you a hint. Comment out the tags that load the Turbo library on both pages.
Don’t forget to comment it on both pages, okay?
Now clear the cache and hard reload the browser by pressing and holding the reload button while the DevTools window is open. This removes the Turbo
library you loaded earlier from the website.

Now click between Home and Contact links.
See something different?
I’m sure you must have figured it out by now. When you navigate between multiple pages, the browser is doing a full reload. You can see this by noticing the earth icon in the tab, which spins for a quick second when you go to a different page.

Now uncomment the script from both pages and reload the browser again. No need to clear the cache this time. The website should fetch the Turbo li
!DOCTYPE>!DOCTYPE>