Speculation Rules API: Zero-Latency Navigation Without an SPA Router
The Speculation Rules API lets an MPA prerender the next page before the click, without the bundle cost or hydration overhead of a client-side router. It ships in Chrome and Edge only - Safari has it behind a disabled flag, Firefox hasn't shipped it - and the eagerness setting determines whether you accelerate real navigations or waste bandwidth speculating on ones that never happen.
Disclosure: We implement these architectures for clients and use them on our own infrastructure.
The SPA-router argument for client-side routing has always been the same: full page navigations feel slow, so eliminate them. Ship one JavaScript bundle, keep the app running, swap DOM subtrees on navigation. The perceived-speed win was real, and it justified a real cost - bundle size, hydration, and every downstream problem covered elsewhere on this site.
The Speculation Rules API removes the justification. It lets the browser prerender the next real page - same origin, real HTML document, real navigation - before the user clicks, so the navigation that does happen is already sitting in memory. You get the SPA’s perceived speed on an actual multi-page architecture, with none of the client-side routing weight.
What it replaces, and what it doesn’t
<link rel="prefetch"> has existed for years. It downloads a document’s bytes into the HTTP cache and does nothing else - no rendering, no subresource loading. <link rel="prerender"> was a Chrome-only predecessor that did a full invisible render, but it was expensive, prone to side effects on pages with analytics or state, and never shipped anywhere else. Chrome deprecated it.
Speculation Rules replaces both with a single declarative API and two distinct actions:
| Action | What happens | Cost | Use for |
|---|---|---|---|
prefetch | Downloads the HTML document body only. No subresources, no JS execution. | Low | Broad application - most links on your site |
prerender | Fetches the document, loads subresources, executes JavaScript, renders into an invisible tab. | High - comparable to rendering an <iframe> | Sparingly - high-confidence next pages only |
That cost asymmetry should drive your rule design. MDN’s own guidance is blunt about it: adopt prefetch broadly, adopt prerender narrowly.
Declarative rules, not URL hardcoding
<script type="speculationrules">
{
"prefetch": [{
"source": "document",
"where": { "href_matches": "/specs/*" },
"eagerness": "moderate"
}],
"prerender": [{
"source": "document",
"where": { "href_matches": "/pricing" },
"eagerness": "eager"
}]
}
</script>
source: "document" means the rule applies to links present in the current document, matched by pattern - you are not hardcoding a URL list that goes stale as pages are added or removed. where.href_matches scopes the rule to a section of the site. eagerness controls the trigger:
| Eagerness | Desktop trigger | Mobile trigger (from Jan 2026) |
|---|---|---|
immediate | On page load | On page load |
eager | 10ms hover | 50ms after link enters viewport |
moderate | 200ms hover, or pointerdown | pointerdown (no hover event) |
conservative | pointerdown only | pointerdown only |
eager on mobile is new as of January 2026 and is the setting most likely to surprise you: it fires from viewport intersection alone, with no interaction. That is appropriate for a small number of high-confidence destinations - a pricing page, a primary CTA - and inappropriate applied broadly, because it starts speculating on links the user is merely scrolling past.
The resource ceiling, and why it protects you from yourself
Chrome enforces hard limits so a careless rule set cannot consume unbounded resources:
immediate/eager: up to 50 concurrent prefetches, 10 concurrent prerendersmoderate/conservative: up to 2 concurrent prefetches, 2 concurrent prerenders (FIFO - new speculations evict the oldest)
Chrome also disables speculation entirely under three conditions: Save-Data mode enabled, Battery Saver with low charge, or the user has turned off the browser’s “Preload pages” setting. These are conditions you cannot override from your rule set, deliberately - the API is built so that aggressive speculation degrades to no speculation on constrained devices rather than degrading the device’s battery or data plan.
Yielding the main thread during prerender
A prerendered page executes its full JavaScript in the background, in an invisible tab. If that JavaScript does anything expensive on load - a large synchronous initialization routine, a big client-side data transform - it competes for the same main-thread budget as your visible tab, on a device that may already be constrained.
Break long synchronous work into yielding chunks so a prerender in progress does not starve the foreground page:
// Anything that runs on page load and could be expensive
// should yield periodically, prerendering or not.
async function processInBackground(items) {
for (let i = 0; i < items.length; i++) {
processItem(items[i]);
// Yield every 50 items so a prerendering tab doesn't
// monopolize the main thread the foreground tab shares.
if (i % 50 === 0) {
await new Promise(resolve =>
'scheduler' in window
? scheduler.yield()
: setTimeout(resolve, 0)
);
}
}
}
scheduler.yield() is the purpose-built API for this; setTimeout(resolve, 0) is the fallback where it is unavailable. Either way, the discipline is the same one that matters for long tasks generally - this is not a Speculation Rules–specific concern, but prerendering makes it a background one you can forget about until it shows up as jank on the next page load.
prerender_until_script: real, but very new
A third action landed in Chrome 144 (January 2026): prerender_until_script. It fetches the document, renders it, and loads CSS/images/fonts - but pauses JavaScript execution at the first blocking <script> tag. The design goal is specific: avoid the side effects of full prerendering - an analytics pageview firing on a page the user never actually visits - while still paying the render and asset cost up front.
Be precise about its status: as of this writing it is a very recent Chrome ship, still working through incubation review at the W3C TAG, with an open explainer document and active discussion. It is real and usable in Chrome 144+ today. It is not yet a cross-browser guarantee, and its interaction with third-party scripts is worth testing directly on your stack before relying on it - a script positioned differently than you expect changes where execution pauses.
<script type="speculationrules">
{
"prerender": [{
"source": "document",
"where": { "href_matches": "/blog/*" },
"eagerness": "moderate"
}]
}
</script>
Chrome 144+ users get prerender_until_script semantics automatically where applicable; there is no separate opt-in syntax to write today beyond the standard prerender rule. Track the explainer if this matters to your stack - the mechanism is still moving.
Browser support - this is not universal, and treating it as such will burn you
| Browser | Support |
|---|---|
| Chrome | prerender key from Chrome 105, prefetch from 110, eagerness/where from 121. Fully shipped. |
| Edge | Same as Chrome - Chromium-based. |
| Safari | Implemented in Safari 26.2. Disabled by default. |
| Firefox | Has declared a positive standards position on the prefetch portion. Not shipped. |
Roughly 79% of global browser traffic on Chromium, by one estimate - check this figure against current caniuse data before quoting it, since it moves.
The API is explicitly designed to degrade safely: unsupported browsers ignore the <script type="speculationrules"> block entirely, with no error and no fallback code required. That makes it a genuine progressive enhancement - ship it once, and Chrome/Edge users get the benefit while Safari and Firefox users get nothing worse than what they had.
What this means for your rollout: speculation rules accelerate a majority of traffic, not all of it. If your audience skews Safari - common for premium consumer products - the measured win on your actual traffic will be smaller than a Chrome-only benchmark suggests. Measure on your own analytics-segmented traffic, not on a global browser-share estimate.
What actually needs auditing before you ship this
Unsafe speculation conditions. The spec defines cases where prerendering a URL is unsafe by default - pages with side effects on load, pages behind auth, pages that mutate state. Chrome applies conservative defaults, but audit your prerender targets specifically: a page that fires a purchase-confirmation event or logs an analytics pageview on load should not be a prerender target, because prerendering executes that code whether or not the user ever arrives.
Cross-site limits. prefetch works across origins with restrictions; prerender largely does not. Design rules around same-origin navigation.
CSP. Speculation rules use a <script> element, so a site with a strict Content-Security-Policy needs inline-speculation-rules added to script-src, via a hash or nonce source, or the rules are silently ignored.
Combining with the rest of your architecture
The highest-leverage pairing on this site is Speculation Rules plus View Transitions on cross-document navigation. A prerendered page that then cross-fades in via a native view transition eliminates both problems SPA routing existed to solve - navigation latency and the jarring hard cut of a full page load - on a genuine MPA, with two declarative browser features and zero client-side routing code.
Frequently asked questions
Does the Speculation Rules API work in Safari and Firefox?
Safari 26.2 has an implementation disabled by default. Firefox has not shipped it. It works fully in Chrome and Edge. The API degrades safely in unsupported browsers - no fallback code is required.
What’s the difference between prefetch and prerender?
prefetch downloads the HTML document only, with no rendering or JavaScript execution - low cost, safe to apply broadly. prerender does a full invisible render including JavaScript execution, at a cost roughly comparable to rendering an <iframe> - reserve it for high-confidence destinations.
Is prerender_until_script shipped and stable?
It shipped in Chrome 144 (January 2026) and is usable today in that browser, but it is recent and still moving through standards review. Treat it as current-but-young rather than settled, and verify its interaction with your third-party scripts directly.
Will this replace my SPA router?
For content sites - marketing pages, documentation, blogs - yes, in combination with an MPA architecture and View Transitions for the cross-fade. For a genuine application with persistent client state, it solves navigation latency but doesn’t address the reasons you have client state in the first place.
Does prerendering execute JavaScript on the target page?
Yes, unless you’re relying on prerender_until_script, which pauses at the first blocking script. Standard prerender executes fully - which is exactly why unsafe-to-prerender pages (anything with load-time side effects) need to be excluded from your rules explicitly.
Sources
- MDN: Speculation Rules API
- MDN:
<script type="speculationrules"> - Uploadcare: Speculation Rules API guide - browser support detail
- corewebvitals.io: Speculation Rules eagerness and resource limits
- W3C TAG design review:
prerender_until_script- incubation status, opened January 2026