12 · side path · the head

The head is merged, not replaced.

Click around. Every response carries a full <head>; the response panel shows it unmarked, unused. Watch the tab title, though: it changes on every visit. That's the merge doing the one thing it needed to. Turbo compares it with the current head: a <title> or <meta> that changed gets updated, a <link> or <script> with a URL that's already loaded is left alone. New scripts in the head are appended and run once.

That's the entire reason Drive is fast: your bundle is parsed once per tab, not once per page.

Inline <script> tags in the body do run on every render. Use data-turbo-eval="false" to stop that, or better, don't put behaviour in body scripts at all.

12 · deploys

What happens when you ship new JavaScript?

A long-lived tab would keep running the old bundle forever. Rails fingerprints assets, so a new deploy means a new URL, and Turbo can notice:

<script src="/app-a1f3c.js" data-turbo-track="reload"></script>

When a response's tracked asset URL differs from the one on the page, Turbo gives up on the merge and does one full page load. Press deploy, then click any link.

The Rails layout helpers do this for you: stylesheet_link_tag "application", "data-turbo-track": "reload".

→ deploy, then click a link
12 · the event that stopped firing

DOMContentLoaded fires once per process.

Look at the console line as you navigate. On the first load it says DOMContentLoaded fired. After that, every Drive visit says turbo:load instead. Any code waiting for the first event runs once and never again. That's the classic "it works after a hard refresh" bug.

// before
document.addEventListener("DOMContentLoaded", setUpTooltips)

// after: fires on first load and after every visit
document.addEventListener("turbo:load", setUpTooltips)

Two catches. Your function must be idempotent, because Back restores a page that was already set up (insert a date header twice and you'll see it twice). And you'll have to clean up what the previous page installed. Both problems disappear if behaviour lives in Stimulus controllers, which connect and disconnect with the elements themselves. That's deck 40.

12 · the escape hatch

Some pages just want a real load.

<meta name="turbo-visit-control" content="reload">

Put it in the head of a page and every visit to that page becomes a full load, whichever link led there. It's the right answer for a third-party widget that assumes a fresh document, and for the login page a frame might get redirected to.

Two neighbours: <meta name="turbo-root" content="/app"> keeps Drive inside a path prefix, and data-turbo="false" on a single link hands that click back to the browser.