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.
| Aspect | Turbolinks 5 | Turbo Drive |
|---|---|---|
| Maintenance | Superseded 2021 | Active - part of Hotwire stack |
| Form handling | GET requests only | All HTTP methods, redirects handled |
| Partial updates | Full <body> swap | Turbo Frames + Streams |
| Event API | turbolinks:load, turbolinks:click | turbo:load, turbo:frame-load |
| Progress bar | Built-in CSS | Built-in + customizable |
| Cache strategy | Snapshot cache | Enhanced snapshot cache with previews |
| Migration path | Direct gem swap + rename events | Can coexist during migration |
Phase 1: The Legacy Turbolinks Fragility
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 Event | Modern Turbo Drive Event | Purpose |
|---|---|---|
turbolinks:load | turbo:load | Initialize components after render |
turbolinks:before-cache | turbo:before-cache | Teardown plugins (destroy instances) |
turbolinks:before-render | turbo:before-render | Modify 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.