Speculation Rules API: The Declarative Way to Make Navigations Feel Instant
Every click still costs 200-800ms of blank-screen time. The Speculation Rules API lets browsers prerender the next page before the click happens — here's what it does and where it goes wrong.
Hanuman Singh
Founder & Lead Engineer · Hanuman Software Services
The problem: every click still costs a round trip
Even on a fast connection, clicking a link triggers a real chain of work — DNS if needed, TCP/TLS, request, server processing, response, parse, render. On a typical marketing or content site that's 200-800ms of blank or loading-state screen per navigation, multiplied by every link a user clicks in a session. Client-side routing in an SPA hides this after the first load, but the very first navigation — and any full page load — still pays it in full.
The Speculation Rules API is the modern, declarative answer: a JSON block that tells the browser which URLs are worth prefetching or fully prerendering before the user clicks, so that by the time they do, the page is already sitting there ready to swap in.
Prefetch vs. prerender — genuinely different things
These two modes get used interchangeably in casual conversation, but the API treats them as distinct with very different costs:
| Mode | What happens | Cost | Navigation feel |
|---|---|---|---|
| Prefetch | Downloads the HTML response only | Low — one request | Faster, but still parses/renders on click |
| Prerender | Fully loads and renders the page in a hidden background tab, including running its JS | High — a full page load's worth of CPU/network | Near-instant — it's a tab swap, not a load |
Prerender is the dramatic one: the target page is actually rendered, off-screen, before the click happens. When the user does click, Chrome activates the already-rendered document instead of starting a new navigation. Done well, this is the difference between a visible loading state and what feels like an instant, client-side-routed transition — on a plain server-rendered page, with no SPA framework involved.
What the rules actually look like
<script type="speculationrules">
{
"prerender": [{
"where": { "href_matches": "/blog/*" },
"eagerness": "moderate"
}],
"prefetch": [{
"where": { "href_matches": "/*" },
"eagerness": "conservative"
}]
}
</script>
eagerness is the important dial: immediate fires as soon as the rule is seen, eager fires on hover/pointerdown, moderate waits for a longer hover, and conservative waits until the user has essentially committed (pointerdown). For prerender specifically, more eager settings mean speculative work happens more often for clicks that never come — that tradeoff is the entire tuning problem.
Where this goes wrong
The API is simple to turn on and easy to misuse. The failure modes we've actually hit:
- Prerendering pages with side effects on load. If a page fires an analytics pageview, increments a view counter, or calls any non-idempotent endpoint on load, prerendering it fires that side effect for every speculative render — including ones the user never actually visits. The
Speculation Rulesspec accounts for this with a documented "prerendering" page-visibility state your code can check, but only if you actually check it. - Prerendering behind auth or personalization checks. A prerendered page runs in a real browsing context, so if it does a client-side auth check or fetches personalized data, that happens during the speculative render too — and if the user never clicks, you've done that work for nothing. Cap eagerness on any page with real per-request cost.
- Over-prerendering on mobile. Prerender is a genuinely resource-heavy operation — full JS execution, full render — and Chrome imposes real limits (typically capping concurrent prerenders and disabling more eager modes under memory pressure or data-saver settings), but you can still waste real user battery and data with an overly broad
href_matchespattern that fires on every link on a content-heavy page.
How to check it's actually working
Chrome DevTools' Application panel has a dedicated Speculative loads section under Background services — it shows which URLs were prefetched or prerendered, their status, and why a speculation was or wasn't used (a "not used" reason here is often the fastest way to debug a rule that isn't firing the way you expect). Check this before assuming your rules are working; a syntax mistake in the JSON block fails silently with no console error.
Should you turn this on now?
Yes, cautiously, for the cheap case first: prefetching your own internal navigation links (blog index → post, product list → product page) is low-risk and broadly safe to ship today. Prerendering is worth it specifically for high-intent, side-effect-free destinations — a pricing page linked from a prominent CTA, the next post in a series — where the near-instant feel is worth the extra resource cost. Start conservative on eagerness, watch the DevTools panel for a week of real traffic, and widen from there rather than guessing at the right scope up front.
Want your site to feel instant?
We audit real navigation patterns before recommending speculation rules — not a blanket "prerender everything" config that quietly wastes bandwidth. Tell us what's slow and we'll tell you honestly whether this is the fix.