10 · Turbo Drive

Turn Turbo on. Change nothing else.

Turbo Drive intercepts clicks on links to your own site. Instead of letting the browser navigate, it fetches the page itself, swaps the <body>, merges the <head>, and updates the address bar without a reload, so Back and Forward still work.

Same links. Same server. Same HTML. The server doesn't even know. In Rails this is the turbo-rails gem, on by default in a new app: one import in application.js, plus the view and model helpers you'll meet along the way.

Place your bet. Type a draft, then click About. Which of the four survive this time?

→ switch turbo on, type, click About
10 · what survived

The process lived. The draft didn't.

⏱ uptime
loadreset
Drivekept
✎ draft
loadlost
Drivelost
↕ scroll
loadtop
Drivetop
▾ panel
loadclosed
Driveclosed

Drive keeps the process: window, document, every script and stylesheet already loaded. It does not keep the elements. The textarea lived inside <body>, and the whole body was replaced.

Look at the response: only <body> is highlighted now. The <head> was merged: the tab title changed, but the stylesheet and script with the same URLs were left alone. That's where the speed comes from. Not from smaller HTML, but from not re-running your CSS and JavaScript on every click.

10 · naming what persists

Tell Turbo what to carry across.

Give an element an id and mark it data-turbo-permanent. Before each render, Turbo finds the element with the same id in the new page and moves your existing node into its place. Listeners, typed text, everything.

<form id="composer" data-turbo-permanent>

One rule: the element has to exist on both pages with the same id. That's why the compose box sits in a dock at the bottom of every page. Flip the switch, type a draft, click About. The form with its own pin, ticked in blue pencil, is the one that travelled.

This is the same attribute you'd use for a music player, a chat sidebar, or anything that must not restart when the page around it changes.

→ switch it on, type, click About
10 · forms too

Post something.

Drive handles forms the same way. It sends the POST itself, expects a 303 redirect back, follows it, and renders the page it lands on. Watch the wire: two round trips, no reload.

On the server this is the redirect_to you already write. Nothing changes.

def create
  @message = Message.create!(message_params)
  redirect_to messages_path, status: :see_other
end

While the request is in flight Turbo disables the submit button so a double-click can't double-post. Add data-turbo-submits-with="Posting…" and it swaps the label too.

→ type a message and press Post
10 · the free stuff

Things you get without asking.

  • A progress bar if a page takes longer than 500 ms. Style .turbo-progress-bar or hide it.
  • Prefetch on hover. Hover a link for 100 ms and Turbo fetches it before you click. Try it: hover About, watch the wire, then click.
  • Opting out. data-turbo="false" on a link, a form, or a whole container hands it back to the browser.
  • Non-GET links. data-turbo-method="delete" plus data-turbo-confirm="Sure?". Try delete on a message.

That's Drive. Now, three questions it raises: