Architecture

Replace Turbolinks with Turbo Drive Smoothly

BLUF (Bottom Line Up Front): Turbolinks is obsolete and replaced by Turbo Drive (part of the Hotwire suite). While the core concept of intercepting links remains the same, the lifecycle events and caching mechanisms differ. Replacing it requires updating the data-turbolinks-track attributes and carefully migrating JavaScript event listeners to the new turbo:load specifications.

Turbolinks 5 vs Turbo Drive (@hotwired/turbo)
AspectTurbolinks 5Turbo Drive
MaintenanceSuperseded 2021Active - part of Hotwire stack
Form handlingGET requests onlyAll HTTP methods, redirects handled
Partial updatesFull <body> swapTurbo Frames + Streams
Event APIturbolinks:load, turbolinks:clickturbo:load, turbo:frame-load
Progress barBuilt-in CSSBuilt-in + customizable
Cache strategySnapshot cacheEnhanced snapshot cache with previews
Migration pathDirect gem swap + rename eventsCan coexist during migration

Turbolinks often caused issues with third-party JavaScript libraries (like datepickers or charting tools) that expected a full page load to initialize.

Synthetic Engineering Context: The Broken Initialization

In a legacy app, you typically see initialization code bound to the Turbolinks load event.

# Legacy JavaScript (Turbolinks)
document.addEventListener("turbolinks:load", function() {
  // Initialize a legacy jQuery plugin
  $('.select2-dropdown').select2();
});

When upgrading the gem to turbo-rails, this code silently fails because the turbolinks:load event is never fired, leaving dropdowns unstyled and non-functional.

Phase 2: The Turbo Drive Transition

The migration is a structural update of attributes and event listeners.

Execution: Mapping Events

Update your Gemfile to replace turbolinks with turbo-rails. Then, systematically replace the JavaScript event hooks across your asset pipeline.

Legacy Turbolinks EventModern Turbo Drive EventPurpose
turbolinks:loadturbo:loadInitialize components after render
turbolinks:before-cacheturbo:before-cacheTeardown plugins (destroy instances)
turbolinks:before-renderturbo:before-renderModify DOM before display
# Modern JavaScript (Turbo Drive)
document.addEventListener("turbo:load", function() {
  $('.select2-dropdown').select2();
});

// Critical: Teardown prevents duplicate instances when restoring from cache
document.addEventListener("turbo:before-cache", function() {
  $('.select2-dropdown').select2('destroy');
});

Execution: Asset Tracking

You must also update the HTML data attributes in your app/views/layouts/application.html.erb layout to ensure assets are tracked correctly during navigation.

<!-- Legacy -->
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>

<!-- Modern -->
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbo-track': 'reload' %>

Phase 3: Next Steps & Risk Mitigation

Swapping Turbolinks for Turbo Drive is usually step one. The real risk lies in complex legacy views that rely heavily on jQuery manipulation. Failing to implement the turbo:before-cache teardowns will result in DOM duplication and severe memory leaks on the client side.

Need Help Stabilizing Your Legacy App? Migrating to Hotwire requires precise control over JavaScript lifecycles. Our team at USEO can refactor your legacy frontend plugins to comply with Turbo Drive mechanics.

Contact us for a Technical Debt Audit