If you’re a Rails developer, chances are that you know Turbolinks. Turbolinks is a flexible and lightweight JavaScript library aimed to make your navigation through webpages faster. Turbolinks improves webpage performance by substituting the common full-page loads for partial loads in multi-page applications.
But how does it accomplish this exactly? Turbolinks works by automatically fetching your page, swapping its DOM’s body, and mixing it with the head
. This allows the library to understand what has changed on the page without having to infer a full load.
Turbolinks intercepts all <a href>
links that would normally direct the user to a page within the application and sends them as requests via AJAX, allowing the body to be replaced with the received content.
Turbolinks focuses on automatic navigation optimization, which means your pages will automatically get faster without the need for too many changes. Turbolinks stores every page you visit within an internal cache, so that when you need to access the same page, Turbolinks can retrieve it from the cache to continue to improve performance. You can also adapt your pages to iOS and Android in hybrid applications to make sure the navigation controls will follow up.
The Turbolinks library can be integrated with HTML pages, Ruby on Rails, Node.js, Laravel, partial fragments, or even JSON. It also touts browser adaptability. Turbolinks intercepts clicks to change behavior behind the scenes, while also helping the browser understand how to get back and forth through the History API.
Turbolinks also brings the immediate benefit of avoiding the famous “blink” effect when navigating from one page to another. And, since Turbolinks relies on HTML5 History API, it is both supported by all modern browsers and also able to degrade to normal navigation in older browsers, without causing any side effects.
In this article, we’re going to explore the Turbolinks library a little bit deeper to understand upsides, downsides, and how your application can benefit. Let’s dive in!
If you’re going with plain HTML and JavaScript applications, simply import the JavaScript file within your head
tag and you’re done.
Because the library was targeted initially at Rails applications, this is the language in which it is most broadly adopted.
To install it in any Ruby on Rails application, just add the respective gem to your Gemfile
:
gem 'turbolinks', '~> 5.2.0'
After you run the bundle install
command, Rails will take care of downloading and installing the gem for you.
It also requires you to add the following comment to the JavaScript manifest file:
//= require turbolinks
If you’re aiming to use it along with Node projects, the installation is even simpler:
npm i turbolinks
Within the JavaScript bundle file, make sure to include the following code snippet:
var Turbolinks = require("turbolinks"); Turbolinks.start();
It’s important to note that Turbolinks works smoother within the Rails environment than in a Node application, for example, because it originated from that kind of environment.
Take the auto redirecting we’ve mentioned. When you redirect to a new page within Rails via redirect_to
, Turbolinks Rails automatically adds a header called Turbolinks-Location
to your requests.
This header is essential for Turbolinks identifying when a given request comes from a redirecting flow that had no help from the server.
On the other hand, when dealing with Node-based applications, there’s no way Turbolinks can identify such a type of action other than receiving this header, which means that your client will have to handle this request manually.
head
vs body
approachesIt is generally accepted that for performance purposes, script
tags must always be included at the bottom of the body
element to allow the whole page to be rendered before loading and processing the JavaScript files.
Turbolinks, however, asks for exactly the opposite, recommending you to place the scripts within the head
tag.
This is because Turbolinks fetches and parses the body of the page, ignoring the head. If you leave the scripts within the body, then every time navigation happens the same scripts will be loaded, again and again.
To avoid blocking the page load and ensure a faster first-page load, you can opt for making the scripts’ importing asynchronous:
<script async src="..."></script>
Alternatively, you can add the defer attribute, which is accepted by most modern web browsers, to make sure that the scripts will be processed only after the page is loaded.
If your scripts change frequently, another good practice is to provide your script
tags with an additional data-turbolinks-track
attribute so it can force full page reloads only when the script bundle has changed.
In terms of analytics libraries, such as Google Analytics (GA), it’s important to pay attention to different elements:
<!-- Google Analytics --> <script> window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date; ga('create', 'UA-XXXXX-Y', 'auto'); ga('send', 'pageview'); </script> <script async src='https://www.google-analytics.com/analytics.js'></script> <!-- End Google Analytics -->
GA usually requires users to load a script
element followed by an analytics.js
JavaScript file. Make sure to always import the former within the body, and the latter within the head. This way, you guarantee that each navigation event will re-feed the right variables for the analytics tool.
Okay … but what happens when the link being requested takes too long to return? That’s a common use case that can occur for several reasons, one of the more common being unexpected network latency.
Turbolinks deals with this problem by enabling a default progress bar. If your page takes longer than 500ms to process, then Turbolinks will display the progress bar with some built-in CSS style that you can overwrite as you please. The delay is also customizable via API.
In the end, a div element is generated with a CSS class called turbolinks-progress-bar
.
Suppose that you’re navigating through an application and you’d like to access the same page from a couple of clicks ago. To do this, you go to your browser history, and click the link for the previous page. Normally, your app would attend to your command by refetching the entire page again from the server.
Wouldn’t it be better not to have to request everything from the backend again? Perhaps if the page was cached?
Imagine now that the data displayed on that same page changes rapidly. Going back to the history and retrieving the page from a cache wouldn’t work because the cache’s data may already be outdated.
For both scenarios, Turbolinks provides a good way to either pick and show the cache’s version directly, or fetch a new copy from the backend in parallel and substituting it on the screen when the request is complete.
This is all possible due to its automatic caching mechanism.
However, if you would like to run some code before the page is cached, it allows this via:
document.addEventListener("turbolinks:before-cache", function() { // your code here });
If you want to disable the feature, simply add a no-cache
directive to a meta
tag:
<meta name="turbolinks-cache-control" content="no-cache">
Another cool feature of Turbolinks is the ability to intercept and customize requests before they are submitted.
Let’s say, for example, that you desire to send an arbitrary id for every request triggered by Turbolinks. For this, you simply need to map the following event listener:
document.addEventListener("turbolinks:request-start", function(event) { event.data.xhr.setRequestHeader("X-My-Custom-Request-Id", "MyId"); });
Neat, right? For more customization possibilities, check out the API Reference.
Turbolinks has been used for quite a long time and has achieved a great level of maturity. With a great community to support it, it had evolved quickly and helped many web developers to achieve better performance for their applications.
Its API Reference is a must-read, so make sure to go through it to understand more about the potential of this small and powerful library, and its ability to improve navigation time and application performance.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
2 Replies to "Turbolinks for faster web navigation"
“Turbolinks improves performance of SPAs (single-page applications) by substituting the common full-page loads for partial loads.”
That’s not true.
Turbolinks has nothing to do with SPAs (applications made with React, Angular, etc.)
SPAs don’t do “common full-page loads”.
Turbolinks is about making multi-page applications render faster.
Hey Random Dev, thanks for reaching out.
You’re right, it was my mistake. The original idea of the post was to be about Turbolinks usage along with SPAs, but then it shifted.
The post is updated now.
Thanks for this! 🙂