we’re diving into Turbolinks and InstantClick, two powerful tools that supercharge webpage loading speeds, especially for server-rendered multi-page applications (MPAs). They minimize resource re-parsing and preload content, making page transitions feel as fast as a single-page application (SPA). We’ll break down their principles, usage, and real-world impact with detailed code examples, guiding you step-by-step to master their speed-boosting magic. Our focus is on practical, technical details to help you maximize page load performance!
Why Page Load Speed Matters
Slow page loads ruin user experience. Studies show that if a page takes over 3 seconds to load, 40% of users will bounce. Google’s PageSpeed tests factor load speed into SEO rankings, meaning slow sites lose both users and traffic. Turbolinks and InstantClick tackle this by optimizing navigation and resource loading, making page switches lightning-fast. Let’s start with Turbolinks and explore their mechanics and implementation.
Turbolinks: Making MPAs Feel Like SPAs
Turbolinks is a JavaScript library, originally built for Ruby on Rails but now standalone, usable in any server-rendered web app. Its core idea is to avoid full page reloads by using AJAX to fetch new page content, replacing the current page’s <body>
and merging the <head>
, eliminating the need to re-parse CSS and JavaScript, thus boosting speed significantly. GitHub: Turbolinks LogRocket Blog
How Turbolinks Works
Turbolinks operates simply:
- Intercepts
<a>
tag clicks, preventing default browser navigation. - Uses AJAX (XMLHttpRequest) to fetch the target page.
- Parses the HTML response, replaces the current
<body>
, and merges differing<script>
or<style>
tags in<head>
. - Updates browser history with
history.pushState
for functional back/forward buttons. - Caches visited pages to speed up repeat navigation.
This means CSS and JS are parsed only on the initial load, with subsequent navigations swapping content only, saving significant overhead. Ful.io: Turbolinks
Installing and Configuring Turbolinks
For a simple HTML project, add Turbolinks:
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Turbolinks Demo</title>
<script src="https://cdn.jsdelivr.net/npm/turbolinks@5.2.0/dist/turbolinks.js"></script>
</head>
<body>
<a href="page2.html">Go to Page 2</a>
<h1>Home Page</h1>
</body>
</html>
<!-- page2.html -->
<!DOCTYPE html>
<html>
<head>
<title>Page 2</title>
<script src="https://cdn.jsdelivr.net/npm/turbolinks@5.2.0/dist/turbolinks.js"></script>
</head>
<body>
<a href="index.html">Back to Home</a>
<h1>Page 2</h1>
</body>
</html>
Add Turbolinks via CDN in <head>
, and it auto-initializes, intercepting link clicks. Run http-server
(npm install -g http-server
), visit index.html
, and click the link. Page transitions are near-instantaneous because Turbolinks only swaps the <body>
.
Turbolinks Events
Turbolinks alters the traditional page load event model, so $(document).ready
(jQuery) or DOMContentLoaded
may not work. Use Turbolinks-specific events:
-
turbolinks:load
: Fired when a page loads (including from cache). -
turbolinks:request-start
: AJAX request begins. -
turbolinks:visit
: Navigation starts on link click.
Example to log page loads:
document.addEventListener('turbolinks:load', () => {
console.log('Turbolinks page loaded!');
});
Add this in a <script>
tag in <head>
, as Turbolinks discards <body>
scripts on replacement. Switching pages logs the message in the console. Coderwall: Turbolinks
Turbolinks Caching
Turbolinks caches pages automatically, speeding up repeat visits. Clicking “Back to Home” loads from cache instantly. Control caching manually:
Turbolinks.clearCache(); // Clear cache
Turbolinks.visit('/page2.html', { action: 'replace' }); // Custom navigation
Google Analytics Compatibility
Turbolinks’ AJAX loading doesn’t trigger Google Analytics page tracking. Handle it manually:
<body>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-XXXXX-Y', 'auto');
document.addEventListener('turbolinks:load', () => {
ga('send', 'pageview');
});
</script>
</body>
Trigger ga('send', 'pageview')
on turbolinks:load
to track page views. Coderwall: Turbolinks
Real-World Example
For a Rails project, add to Gemfile
:
gem 'turbolinks', '~> 5.2.0'
In app/assets/javascripts/application.js
:
//= require turbolinks
In app/views/layouts/application.html.erb
:
<!DOCTYPE html>
<html>
<head>
<%= javascript_include_tag 'application' %>
</head>
<body>
<%= link_to 'Page 2', page2_path %>
<%= yield %>
</body>
</html>
Run rails server
. Navigation is noticeably faster than traditional page loads, as CSS and JS load only once.
Turbolinks Notes
-
Script Placement: Place scripts in
<head>
to avoid re-execution. LogRocket: Turbolinks -
Event Binding: Use
addEventListener
instead of jQuery’s$(element).click
to prevent duplicate bindings. - Third-Party Libraries: Libraries like Google Analytics require manual event triggers.
InstantClick: Preloading on Hover
InstantClick is similar to Turbolinks but excels with preloading: it starts loading pages on mouse hover (mouseover
) or touch (touchstart
), so when users click, the page is ready, feeling instantaneous. InstantClick
How InstantClick Works
InstantClick’s core:
-
Preloading: On
mouseover
(ortouchstart
on mobile), uses<link rel="prefetch">
or AJAX to load the page. - Replaces
<body>
and<title>
viapushState
and AJAX (pjax), skipping CSS/JS parsing. - Includes a progress bar for feedback on slow loads.
- Mobile support with
touchstart
preloading (~300ms on Android, ~450ms on iOS). InstantClick 3.0
Installing and Configuring InstantClick
Add InstantClick to an HTML project:
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<title>InstantClick Demo</title>
<script src="http://instantclick.io/instantclick.js" data-no-instant></script>
</head>
<body>
<a href="page2.html">Go to Page 2</a>
<h1>Home Page</h1>
<script>
InstantClick.init();
</script>
</body>
</html>
data-no-instant
prevents InstantClick from processing its own script. Run http-server
, click a link, and the page switches ultra-fast, as InstantClick preloads on hover.
InstantClick Events
InstantClick events include:
-
instantclick:change
: Fired when the page switches. -
instantclick:preload
: Fired when preloading starts.
Example:
InstantClick.on('change', () => {
console.log('InstantClick page changed!');
});
Add to a <script>
tag to log page changes.
Custom Preloading
Set a preload delay to reduce server load:
InstantClick.init(100); // Preload after 100ms hover
For mobile, use mousedown
or touchstart
:
InstantClick.init('mousedown'); // Preload on mouse down
Progress Bar
InstantClick includes a faux progress bar (like NProgress), customizable with CSS:
#instantclick {
background-color: #007bff;
height: 4px;
}
The bar appears during slow loads, giving user feedback. InstantClick 3.0
Google Analytics Compatibility
Like Turbolinks, InstantClick requires manual Analytics tracking:
<body>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-XXXXX-Y', 'auto');
InstantClick.on('change', () => {
ga('send', 'pageview');
});
</script>
</body>
Real-World Example
In a WordPress site, install the InstantClick plugin:
# Assuming a WordPress environment
wp plugin install instantclick --activate
Configure the plugin, setting data-no-instant
on scripts like Analytics. Navigation becomes noticeably faster, with hover preloading making switches feel instant. Keetrax: InstantClick
InstantClick Notes
-
Script Conflicts:
<body>
scripts may re-execute; usedata-no-instant
to exclude them. Keetrax: InstantClick - Preload Overhead: Hover preloading may increase server requests; use a delay (e.g., 100ms) to balance.
-
Mobile:
touchstart
preloading is mobile-friendly but requires optimized server responses.
Combining Turbolinks and InstantClick
Turbolinks and InstantClick share similar goals, but InstantClick’s preloading is more aggressive. Combining them—using InstantClick’s preloading with Turbolinks’ page replacement—can be powerful. Here’s a try:
<!DOCTYPE html>
<html>
<head>
<title>Turbolinks + InstantClick</title>
<script src="https://cdn.jsdelivr.net/npm/turbolinks@5.2.0/dist/turbolinks.js"></script>
<script src="http://instantclick.io/instantclick.js" data-no-instant></script>
</head>
<body>
<a href="page2.html">Go to Page 2</a>
<h1>Home Page</h1>
<script>
document.addEventListener('turbolinks:load', () => {
InstantClick.init(100);
InstantClick.on('change', () => {
console.log('Page changed with InstantClick!');
});
});
</script>
</body>
</html>
Issue: Turbolinks and InstantClick’s AJAX mechanisms can conflict, causing duplicate requests. A community solution uses hoverintent
for preloading:
document.addEventListener('turbolinks:load', () => {
const links = document.querySelectorAll('a:not([data-no-turbolink])');
links.forEach(link => {
link.addEventListener('mouseover', () => {
setTimeout(() => {
const prefetch = document.createElement('link');
prefetch.rel = 'prefetch';
prefetch.href = link.href;
document.head.appendChild(prefetch);
}, 100);
});
});
});
This code adds <link rel="prefetch">
on hover after a 100ms delay, mimicking InstantClick’s preloading while letting Turbolinks handle page replacement. Run it, and hover preloading plus Turbolinks’ efficiency makes clicks feel instant. Mskog: Turbolinks + Prefetch
Performance Comparison
Test Turbolinks and InstantClick with a simple MPA.
Test Pages
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Performance Test</title>
<script src="https://cdn.jsdelivr.net/npm/turbolinks@5.2.0/dist/turbolinks.js"></script>
<script src="http://instantclick.io/instantclick.js" data-no-instant></script>
<style>
body { font-family: Arial; }
.container { max-width: 800px; margin: 0 auto; padding: 20px; }
</style>
</head>
<body>
<div class="container">
<a href="page2.html">Go to Page 2</a>
<h1>Home Page</h1>
<p>Large content...</p>
</div>
</body>
</html>
<!-- page2.html -->
<!DOCTYPE html>
<html>
<head>
<title>Page 2</title>
<script src="https://cdn.jsdelivr.net/npm/turbolinks@5.2.0/dist/turbolinks.js"></script>
</head>
<body>
<div class="container">
<a href="index.html">Back to Home</a>
<h1>Page 2</h1>
<p>Large content...</p>
</div>
</body>
</html>
Add a large image to simulate resource loading:
<img src="https://via.placeholder.com/2000x1000" alt="Test">
Testing Turbolinks
Use only Turbolinks in index.html
, run http-server
, and measure with Chrome DevTools’ Network panel:
- Initial load: ~500ms (CSS, JS, image).
- Navigate to page2.html: ~200ms (only HTML and image).
Turbolinks skips CSS/JS parsing, significantly boosting speed.
Testing InstantClick
Use only InstantClick:
<script>
InstantClick.init(100);
</script>
Test:
- Hover 100ms to preload, click switches in <50ms (page cached).
- Initial load ~500ms, similar to a standard page.
InstantClick’s preloading makes clicks feel instantaneous, like an SPA.
Combined Test
Use the Turbolinks+hoverintent
code:
- Hover preloading + Turbolinks replacement, click switches in <50ms.
- Initial load ~500ms, subsequent navigations ultra-fast.
The combination achieves near-zero perceived delay, with preloading handling content fetch and Turbolinks optimizing replacement. Mskog: Turbolinks + Prefetch
Real-World Scenario: Complex Rails App
Simulate a blog app with Rails, Turbolinks, and InstantClick.
Rails Setup
Create a Rails project:
rails new blog-app
cd blog-app
Add to Gemfile
:
gem 'turbolinks', '~> 5.2.0'
In app/assets/javascripts/application.js
:
//= require turbolinks
Generate a controller and views:
rails generate controller Posts index show
In app/views/layouts/application.html.erb
:
<!DOCTYPE html>
<html>
<head>
<%= javascript_include_tag 'application' %>
<script src="http://instantclick.io/instantclick.js" data-no-instant></script>
<%= stylesheet_link_tag 'application', media: 'all' %>
</head>
<body>
<nav>
<%= link_to 'Home', posts_path %>
<%= link_to 'Post', post_path(1) %>
</nav>
<%= yield %>
<script>
InstantClick.init(100);
document.addEventListener('turbolinks:load', () => {
const links = document.querySelectorAll('a:not([data-no-turbolink])');
links.forEach(link => {
link.addEventListener('mouseover', () => {
setTimeout(() => {
const prefetch = document.createElement('link');
prefetch.rel = 'prefetch';
prefetch.href = link.href;
document.head.appendChild(prefetch);
}, 100);
});
});
});
</script>
</body>
</html>
In app/views/posts/index.html.erb
:
<h1>Blog Posts</h1>
<% 10.times do |i| %>
<p>Post <%= i + 1 %>: <%= link_to 'View', post_path(i + 1) %></p>
<% end %>
In app/views/posts/show.html.erb
:
<h1>Post <%= params[:id] %></h1>
<p>Large content...</p>
<img src="https://via.placeholder.com/2000x1000" alt="Post">
Run rails server
and visit localhost:3000/posts
:
- Initial load ~600ms (includes image).
- Hover links for 100ms to preload, click switches in <50ms.
- Back/forward uses Turbolinks cache, nearly instant.
Performance Analysis Tools
Use Chrome DevTools’ Performance panel:
-
Timeline: Check
turbolinks:load
andinstantclick:change
events for switch times. -
Network: Observe AJAX requests and
<link rel="prefetch">
preloading.
Run Lighthouse for performance:
- Turbolinks: First Contentful Paint (FCP) ~200ms for subsequent pages.
- InstantClick: Click-to-switch <50ms, perceived speed is exceptional.
Conclusion (Technical Details)
Turbolinks and InstantClick make MPA navigation as fast as SPAs using AJAX and preloading. Turbolinks replaces <body>
, skipping CSS/JS parsing; InstantClick preloads on hover for near-instant clicks. Combining them achieves near-zero perceived delay. The code examples demonstrated:
- Turbolinks installation, events, caching, and Analytics integration.
- InstantClick preloading, progress bar, and mobile support.
- Combining both with
hoverintent
for efficient preloading.
Run these examples, check the timeline in DevTools, and experience the silky-smooth page transitions!
Top comments (0)