
Stop Micro-Managing Prefetching: The Speculation Rules API Is Smarter Than Your Script
The browser now knows what your user will click before they do, rendering your custom hover-intent scripts and manual link prefetchers completely obsolete.
How many hours have we collectively wasted writing "smart" hover-intent scripts just to save a few hundred milliseconds of perceived latency?
For years, the gold standard of web performance was trying to predict the future. We used libraries like Instant.page or Quicklink to watch a user's mouse. The logic was simple: if a user hovers over a link for more than 65ms, they’re probably going to click it, so let's start downloading the data now. It worked, but it was a bit of a hack. You had to manage script execution, worry about flooding the network, and handle the cleanup yourself.
Enter the Speculation Rules API. It’s a mouthful of a name, but it’s essentially the browser saying, "I’ve got this. Put your JavaScript away."
It's Not a Hint, It's a Rule
We’ve had <link rel="prefetch"> for ages. The problem is that prefetch is just a suggestion. The browser looks at it, checks its schedule, and says, "Maybe if I have time." It only fetches the main resource, not the CSS or JS required to actually render the page.
Speculation Rules are different. They allow you to define JSON-based instructions that tell the browser exactly what to prefetch—or better yet, prerender.
When you prerender a page, the browser literally renders the entire page in a hidden background tab. When the user finally clicks the link, the current page is swapped for the prerendered one instantly. We’re talking 0ms "Largest Contentful Paint" territory.
Here is what a basic rule looks like:
<script type="speculationrules">
{
"prerender": [
{
"source": "list",
"urls": ["/about", "/contact"]
}
]
}
</script>Letting the Browser Decide When to "Go"
The real magic isn't just in *what* we fetch, but *when*. Manual scripts are usually binary—either you're hovering or you're not. The Speculation Rules API has built-in "eagerness" settings that let the browser's internal engine make the call based on the user's device and connection.
- `immediate`: Do it right now.
- `eager`: Do it as soon as there's a tiny bit of evidence the user might want this (like moving the mouse toward a link).
- `moderate`: Do it on hover or pointer-down.
- `conservative`: Only do it on pointer-down (click/touch start).
I personally love the moderate setting. It's the sweet spot for most sites.
{
"prerender": [
{
"where": { "href_matches": "/products/*" },
"eagerness": "moderate"
}
]
}In this snippet, the browser handles all the mouse-tracking logic. It knows if the user is on a low-end Android phone or a beefy MacBook Pro. If the phone is melting or the data plan is restricted, the browser simply ignores your rule to save the user's battery. You don't have to write a single line of "if-mobile-and-not-low-battery" logic.
The "Gotchas" (Because Nothing is Free)
Before you go wrapping your entire index.html in speculation rules, there are some rules of the road.
First, prerendering is resource-intensive. You’re basically running two browser tabs at once. If you prerender five different pages, you're going to destroy the user's RAM. Stick to the "next likely step" rather than a "shotgun" approach.
Second, there’s the side-effect problem. Since prerendering actually executes the page, your useEffect hooks or analytics scripts will fire. If you aren't careful, your Google Analytics will show a 100% conversion rate because every user "visited" the checkout page before they even decided to buy.
You can check if a page is currently being prerendered using the document.prerendering API:
if (document.prerendering) {
// Don't fire the "Purchase Complete" event yet!
document.addEventListener('prerenderingchange', () => {
if (!document.prerendering) {
initAnalytics();
}
}, { once: true });
} else {
initAnalytics();
}Why This Matters Now
Right now, this is primarily a Chromium feature (Chrome, Edge, Brave). But that represents a massive chunk of web traffic. For users on other browsers, the script is just an ignored block of JSON, so there's zero penalty for including it.
I've found that swapping out a custom "hover-intent" library for a few lines of Speculation Rules JSON not only improved my site's "Instant-feel" but also deleted about 2KB of redundant JavaScript from my bundles.
Stop trying to build a better crystal ball in JavaScript. The browser is already sitting in the front row of the user's behavior; let it do the heavy lifting.

