Improving the loading speed of web pages is often about making series of small incremental improvements: better CSS minification here, higher compression setting there, reducing JavaScript dependencies and so on. Rarely we see an opportunity that has the potential of altering “the game” completely. This post is about one such opportunity of not just making pages load 10% or even 50% faster, but rather make them appear to load immediately.
At the recent Google I/O, announcements around the re-invigoration of page prerendering made quite a splash among the web performance-minded developers. There is a blog post that’s a good start, providing an overview of the Speculation Rules API and some context of how we got here. This post is about trying out the new API in the good ol’ console, just checking it out commitment-free, seeing-is-believing style.
What is the Speculation Rules API?
Imagine if the browser didn’t have to fetch all the resources and render the new page when you click on a link to load a new page. Because… it had already done it! That’s what the Speculation Rules API is all about. It’s bit of JSON data you put inside a <script>
tag with a special attribute that contains rules on pages you want to do this rendering ahead of time on. This information can be updated in real time as needed. The point is making new page loads feel instant. It’s a great tool for web performance and thus happy users.
It’s as if you’ve loaded a new page in a hidden iframe and then swapped with the original page when necessary. Except, there are no iframes involved, it’s all done by the browser and comes with a nice API to boot.
According to its specification, the Speculation Rules API is not yet a standard nor it is on the W3C standards track. However it’s already publicly available in various Chromium browsers, totaling over 70% global reach (source: caniuse.com)
Potential Confusion with Prior “Pre” Usage
There’s a potential for confusion here because we have overused the “pre” prefix over the years. We have the <link>
’s rel attribute for example, with values preload
, prefetch
, preconnect
, which all do different things. Those will still exist.
One last thing before we begin: I’m using a WordPress blog as a playground because many of us have seen or managed a blog at some point and it’s familiar. However, if you want to use the new API without a care in the world, you can simply install the existing WP plugin created by WP’s perf team.
Adventure Time
Let’s play with Speculation Rules API, all in the browser console, with no server-side changes necessary.
Step 1) Load a web page, e.g. https://phpied.com
Step 2) Open the browser console and add a new class name to three links of your choosing. In this case the class name is prerenderosa
and the links of my choosing are the links to the latest 3 blogposts.
document.querySelectorAll('.entry a[rel=bookmark]')
.slice(0, 3)
.forEach(e => e.classList.add('prerenderosa')
);
Code language: JavaScript (javascript)
Step 3) Set up the speculation rules JavaScript object:
const pre = {
"prerender": [{
"source": "document", // optional, since Chrome 121
"where": {
"and": [
// prerender pages where the link has the new class name
{ "selector_matches": ".prerenderosa" },
]
},
"eagerness": "immediate", // be eager!
}]
};
Code language: JavaScript (javascript)
Step 4) Add the speculations rules to the page, using a script
element.
const spec = document.createElement('script');
spec.type = "speculationrules";
spec.append(JSON.stringify(pre));
document.body.append(spec);
Code language: JavaScript (javascript)
And that’s it! Pages are prerendered. Clicking on any of the 3 links shows the page loaded immediately.
Debugging
How do we know things worked as expected and what happened exactly? DevTools to the rescue. Open the Application tab and find “Speculative loads” in the menu. This is where you can inspect the results.
Prerendering Prerequisites
There are conditions to be met before the prerendering can happen. Most importantly you need to check if any extensions have disabled prefetching. This is common with ad-blocking extensions. This means disable the setting for all sites, not just disable the extension on the current page. For example in uBlock Origin, unselect the option called “Disable pre-fetching”. Research by DebugBear into extensions that harm performance points to two other additional popular extensions (where “popular” means over 1 million installs): Windscribe and Privacy Badger.
Luckily, DevTools provides a link to the extension management area as well as the preload settings (they were off by default for me).
Below is the view you see when you click on “Preload pages settings” link. Note that the “standard” preload setting is good enough, no need for the “extended”.
You may notice that the toggle is off and disabled so you cannot turn it on. This is a sign that possibly an extension is preventing it. In this case you should be able to see a little puzzle icon. Mouse over that icon should reveal who’s disabling the setting.
Note that prerendering won’t work if the link is opened in a new browser window/tab (e.g. with target="_blank"
). But the benefit of the downloaded resources of the new page (scripts, styles, images) still exists. And support for window targets is coming soon.
Next Steps in Debugging
Now let’s see what happens every step of the way. First we load a page and pop open the new “Speculative loads” and the console.
Now we paste the generic code from above: the one that adds class names to three selected links and adds the speculationrules
script element.
Lo and behold: 3 pages are prerendered and ready.
You can click to the “Speculations” submenu to see which pages were prerendered.
Also in the Network panel you can now pick which waterfall to explore: the original page or any of the preloaded ones.
If you click on a link to a prerendered page, the load is immediate, because it’s just a swap.
On the next page you can also see that the “Speculative loading status” is a success. This is the prerendered page. It doesn’t have any speculation of its own but was itself loaded speculatively.
Everything in Moderation
The snippet above had "eagerness": "immediate"
speculation rule. But you have other options. Let’s try moderate
. We keep everything the same, just change one setting:
const pre = {
"prerender": [{
"source": "document",
"where": {
"and": [
{ "selector_matches": ".prerenderosa" },
]
},
"eagerness": "moderate", // easy there tiger
}]
};
Code language: JavaScript (javascript)
Now nothing is prerendered, until you mouseover for 200ms over a link that is a prerendering candidate. And as you can see out of the three candidates for prerendering one is indeed prerendered, the other two are not.
This is much less aggressive and easy on your server.
There’s one more eagerness option: “conservative”. With it, the browser only prerenders on mouse/pointer down. There’s still an early start but the effect may not be the same.
Concerns?
If you’re like me, you probably have a lot of questions for example:
- Can I opt out analytics of prerendered-but-never-seen pages?
- What about performance measurements?
- Can I have an API to know when prerendering happens?
I’m happy to say that these concerns are all addressed (see here) and reportedly already supported by Google Analytics.
Go forth and speculate!
As you can see, you can use the new goodness purely on the client-side. There are other options, naturally you can have the speculation rules server-generated and spit out in the HTML. Or you can also have an HTTP header that points to a separate JSON file with all the rules.
But no matter how you do it, the results speak for themselves and the Speculation Rules API is definitely worth exploring.
As it’s progressive enhancement it can be used right now – no need to wait for Safari to catch up (if ever) 😁