This demo project uses Astro (based on Astroship template) to build and run the development preview
pnpm i
pnpm dev
For a more real-world experience make sure to build the site and preview
pnpm build
pnpm preview
As of the time of writing (October 2024),
the Speculation Rules API
is an experimental
feature available in all Chromium-based browsers.
It replaces
the now-deprecated <link rel="prerender">
and <link rel="prefetch">
.
The Speculation Rules API is designed to enhance navigation performance by pre-rendering or
pre-fetching specific resources. It primarily relies on href
matching rules to identify which
pages can be pre-rendered in the background or which resources can be pre-fetched.
This feature benefits Multi-Page Applications (MPAs) by speeding up future navigations, while
Single-Page Applications (SPAs) mainly benefit during initial page loads. Speculation rules can be
implemented either within an HTML <script>
tag or via a "speculation-rules" response header.
Pre-rendering allows browsers to pre-fetch, render, and load a page in an invisible tab, including all subresources and JavaScript. This makes future navigations to the pre-rendered page nearly instantaneous since the browser activates the hidden tab rather than reloading the page from scratch.
Pre-fetching, on the other hand allows browsers to download the response body of referenced pages
without fetching subresources, improving load times when users navigate to those pages. This method
is more efficient than older approaches like <link rel="prefetch">
or fetch()
with low priority,
as it supports cross-site navigation and avoids being blocked by Cache-Control
headers.
In this article, we won't cover all the features, and since the API is still experimental, there may be updates and improvements in the near future. I recommend checking the MDN docs for the latest information.
The simplest way to define speculation rules is in a script tag within the current document. For example:
<script type="speculationrules">
{
"prerender": [
{
"where": {
"and": [
{"href_matches": "/*"},
{"not": {"href_matches": "/logout"}},
{"not": {"href_matches": "/*\\?*(^|&)add-to-cart=*"}},
{"not": {"selector_matches": ".no-prerender"}},
{"not": {"selector_matches": "[rel~=nofollow]"}}
]
},
"eagerness": "moderate"
}
],
"prefetch": [
{
"urls": ["next.html", "next2.html"],
"requires": ["anonymous-client-ip-when-cross-origin"],
"referrer_policy": "no-referrer"
}
]
}
</script>
The first example that came to me when writing this post and thinking about use cases for speculative pre-rendering was blogs. For instance, if a blog provides a next button to load the following article, pre-rendering the next page could make navigation simple for the reader.
Wiki pages would be an excellent application as well. Because wikis commonly contain links that lead to related articles, speculative pre-rendering could improve user experience by preloading popular links.
Generally speaking, implementing speculative rules into any multi-page application (MPA) could increase user happiness.
Having instant page navigations and near-zero milliseconds loading times sounds ideal, but there are some clear downsides to consider.
First, both pre-rendering and pre-fetching consume resources. Background requests increase the load on servers and also use up the client’s bandwidth. Rendering a page in the background consumes processing power, which can be especially taxing on mobile devices, potentially leading to increased battery drain.
Second, there are potential security concerns with pre-rendering documents, particularly when JavaScript executes automatically. This can open up risks where simply navigating to a page that contains a malicious link could trigger harmful scripts, eliminating the need for a user to actively click on the link. This makes pre-rendering a potential vector for exploitation.
I’ve created a demo blog site (GitHub) that includes a landing page, an about page, and a blog page where all the posts are displayed. The site also uses simple speculation rules to demonstrate how pre-rendering works:
<script id="speculationrules" type="speculationrules">
{
"prerender": [{
"source": "document",
"where": {
"and": [
{"href_matches": "/*"},
{"not": {"href_matches": "*-no-prerender*"}}
]},
"eagerness": "eager"
}]
}
</script>
Note: Currently, the blog page pre-renders all posts that match the speculation rules when it loads. In a real-world scenario, you'd likely want to adjust the eagerness setting to balance resource usage and performance.
The eagerness setting determines when speculation rules are triggered.
- An eagerness of "eager" means that as soon as the page loads, it pre-renders every page that matches the speculation rules.
- An eagerness of "moderate" delays pre-rendering until a user interacts with the page—specifically, when hovering over a link that meets the speculation rules criteria.
This setting helps balance resource usage and performance based on user interaction. More about eagerness
To see this in action, ensure you're using a supported browser and that your browser's "Preload pages" setting is not turned off (unfortunately some extensions interfere with this setting), or just use an incognito tab.
- Navigate to the demonstration blog site
- Open dev tools (F12)
Open the Application tab and find the Background services section ⇾ Speculative Loads ⇾ Rules, where you can view the active speculation rules.
Right next to Rules there's the Speculations section, you’ll see all the pages that have been pre-rendered or what state they are in. As they are in "Ready" state, when navigating, the page will load instantly.
You can also observe background activity in the Network tab, where you’ll notice the pre-rendering process at work.
Some links are intentionally excluded from pre-rendering. These are identified by URLs ending with " -no-prerender" and are also labelled with "NO PRERENDER:" in the link text for easy identification.
In the top-left corner of each page, you'll find a "Predictive Pre-rendering Stats" box displaying the page's load time (measured by Largest Contentful Paint, or LCP). While not 100% accurate, it’s close enough for demonstration purposes.
By clicking through blog posts and checking the LCP time, you may not notice a speed improvement if you're on a fast network. If that’s the case, I recommend throttling the network speed in DevTools:
- In the Network tab, change the dropdown from No throttling to Slow 4G or even slower.
- Test the pages again and compare load times.
If the performance boost isn’t immediately obvious, it might be because the pre-rendering wasn't completed when the link was clicked. You can confirm the pre-render status in the Speculations section under the Application tab.
In my tests, switching to Slow 4G resulted in about 2000ms for non-pre-rendered pages versus 100ms for pre-rendered ones.
In summary, the Speculation Rules API offers exciting opportunities to enhance the performance of Multi-Page Applications (MPAs) by intelligently pre-rendering or pre-fetching content, making future navigations feel nearly instantaneous. With proper implementation, this API can significantly improve user experience.
However, developers should be cautious about the downsides, such as increased resource consumption and security risks. Balancing these trade-offs with appropriate usage of the eagerness setting and carefully defined speculation rules can ensure a smoother, faster, and safer browsing experience.
Unfortunately speculation rules are currently only available in Chromium-based browsers and even there an experimental feature, therefore subject to change. I would not recommend to use it outside hobby-projects yet.
Sources: