Speculation Rules API

Prefetch vs. Prerender

By default the browser does nothing until you click, then kicks off the full sequence from scratch: DNS, TCP, TLS, HTML fetch, parse, JS execute, render. All of that happens after you’ve committed to navigating.

Both prefetch and prerender move that work earlier, but they stop at different points.

Prefetch stops after the network. It downloads the HTML and sub-resources into cache, but the page isn’t parsed or rendered. You still pay the rendering cost on click, just not the network cost.

Prerender goes further. It fetches, parses, executes JS, lays out, and paints in a hidden background tab. When you click, the browser swaps the already-rendered page in.

Prefetch strategies

StrategyTriggers on
tapmousedown, just before click
hoverhover delay (~80ms)
viewportlink scrolls into view
loadevery link, immediately on page load

Prerendering

You can trigger it per-link with rel="prerender":

<link rel="prerender" href="/about" />

The opposite end is lazy loading: defer everything until explicitly needed (loading="lazy", dynamic imports, IntersectionObserver).

The Speculation Rules API

Chrome 109+ lets you control this with a JSON script tag:

<script type="speculationrules">
  {"prerender":[{"where":{"href_matches":"/*"},"eagerness":"moderate"}]}
</script>

eagerness controls when the browser triggers prerendering:

ValueTriggers on
conservativemousedown
moderatehover / focus
eagerlink in DOM

moderate is the right default. Hover is clear enough intent, and a wrong guess just wastes a bit of bandwidth.

Prefetch + prerender compose well

Astro’s prefetch: { defaultStrategy: 'viewport' } fires when a link scrolls into view, warming the cache early. By hover time the assets are already local, so the prerender finishes fast:

link enters viewport → prefetch (network)

         user hovers → prerender (full render, from cache)

            click → instant swap

Other browsers silently ignore the script tag, so no broken behaviour as a fallback.