Master the Speculation Rules API for predictive prefetching. Learn how to drive instant navigation and improve Core Web Vitals without breaking your server.
We’ve all spent hours obsessing over the critical rendering path to shave off a few milliseconds, only to realize the biggest bottleneck is the round-trip time of the initial request. When a user clicks a link, the browser has to start from scratch. What if we could stop waiting for that click and start loading the next page before the user even moves their mouse?
That’s where the Speculation Rules API comes in. It’s a browser-level primitive that allows us to tell the browser which pages are likely to be visited next, letting the browser handle the prefetching and prerendering in the background.
Most performance tuning focuses on what happens after the request hits the server. But predictive prefetching changes the game by eliminating the network latency entirely.
When you implement this correctly, the next page load feels instantaneous. It’s the closest we’ve gotten to the "instant-app" feel on the web. By pre-loading documents, we aren't just saving time; we're fundamentally altering how the user perceives the speed of our application.
The API uses a JSON-based configuration injected into the <head> of your document. It’s declarative, which is a massive relief compared to manually managing <link rel="prefetch"> tags with JavaScript.
Here is a basic setup to prefetch links that match a specific URL pattern:
HTMLstyle="color:#808080"><style="color:#4EC9B0">script type="speculationrules"> { "prefetch": [ { "source": "list", "urls": ["/dashboard", "/settings", "/profile"] } ] } style="color:#808080"></style="color:#4EC9B0">script>
This tells the browser: "I'm fairly certain the user is heading to one of these three places, so start fetching them now." The browser handles the priority, ensuring it doesn't starve the current page of resources.
I learned the hard way that "predictive" doesn't mean "aggressive." In an early experiment, I configured the rules to prefetch every link in the navigation menu. My server logs lit up with 404s and excessive traffic from users who were just hovering over the menu but never intended to click.
You have to be surgical. If you prefetch too much, you’re wasting your users' data and hammering your own backend.
Here are the rules I follow now:
eagerness: You can set the eagerness property to conservative, moderate, or eager. Start with conservative to trigger fetches only on mouse-down or hover.If you want the "instant" experience, you move beyond prefetch and use prerender. This actually renders the page in a hidden background tab. When the user clicks, the browser swaps the hidden tab with the visible one.
HTMLstyle="color:#808080"><style="color:#4EC9B0">script type="speculationrules"> { "prerender": [ { "source": "document", "where": { "and": [ { "href_matches": "/*" }, { "not": { "href_matches": "/logout" } } ] }, "eagerness": "moderate" } ] } style="color:#808080"></style="color:#4EC9B0">script>
This is incredibly powerful for Core Web Vitals. Because the page is already rendered, the Largest Contentful Paint (LCP) effectively drops to near zero.
Does this work on all browsers? It’s a Chromium-based feature. For other browsers, you’ll need a fallback mechanism, though they will simply ignore the script tag, so it won't break your site.
Will this cause issues with analytics?
Yes, if you aren't careful. A prerendered page might trigger a pageview event before the user actually sees it. Ensure your analytics provider is aware of the document.prerendering state and only fires events when visibilityState becomes visible.
How do I debug these rules? Open Chrome DevTools, go to the "Application" tab, and look for "Speculative Loads" in the sidebar. It gives you a clear view of which rules are active and whether they succeeded or failed.
The Speculation Rules API is a tool, not a silver bullet. If your underlying site is slow, prefetching just hides the symptoms rather than fixing the disease. Make sure your WordPress Transients API or other caching layers are solid first.
I’m still experimenting with how to handle dynamic, user-specific data in prerendered pages. It’s tricky because you don't want to serve stale, personalized content. For now, I stick to prefetching static or semi-static routes. I’d love to hear how you’re handling personalized state in your own implementations—it’s the next frontier for this tech.
Master Web Workers and OffscreenCanvas to offload heavy rendering tasks from the main thread. Learn how to keep your UI responsive and hit your performance budget.