Speculation Rules
Pages that open instantly because the browser prepares them before the click.
A <script type="speculationrules"> block (or the Speculation-Rules header) says which links to prefetch or fully prerender in the background, and how early: immediate, eager, moderate (on desktop, 200 ms hover or pointer down; on mobile, viewport heuristics) or conservative (pointer down).
Chromium only: Chrome and Edge since 109 (document rules and eagerness since 121). Safari 26.2 has prefetch behind a flag and Firefox does not implement it. It is a progressive enhancement: where unsupported, links load as usual.
What it is for
- Having the next step of a checkout ready
- Instant navigation on content sites
- Prerendering the most likely search result
Demo
This page asks to prerender destination A as soon as it loads. B loads normally. Open both and compare: the destination page tells you whether it was prerendered.
<script type="speculationrules">
{
"prerender": [
{
"urls": [
"/en/speculation-rules/destination-a/"
],
"eagerness": "immediate"
}
]
}
</script>Browser support
Limited availability| Feature | Desktop | Mobile | |||||
|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Safari | Chrome Android | Firefox Android | Safari iOS | |
| <script type="speculationrules"> | 109Chrome: 109 | 109Edge: 109 | —Firefox: No | 26.2flagSafari: 26.2 flag | 109Chrome: 109 | —Firefox: No | 26.2flagSafari: 26.2 flag |
| prerender | 105Chrome: 105 | 105Edge: 105 | —Firefox: No | —Safari: No | 103Chrome: 103 | —Firefox: No | —Safari: No |
| prefetch | 110Chrome: 110 | 110Edge: 110 | —Firefox: No | 26.2flagSafari: 26.2 flag | 103Chrome: 103 | —Firefox: No | 26.2flagSafari: 26.2 flag |
Compatibility generated on every deploy from MDN browser-compat-data 8.1.3 (24 Sep 2026) · web-features 3.40.0 · checked by hand: 1 Oct 2026
Minimal code
<!doctype html>
<meta charset="utf-8">
<title>Speculation Rules · ejemplo mínimo</title>
<!-- Prerenderiza los enlaces de esta web al pasar el ratón (200 ms) o al empezar a pulsar.
Chromium 109+. Sírvelo por HTTP (npx serve .). -->
<script type="speculationrules">
{
"prerender": [{
"source": "document",
"where": { "and": [
{ "href_matches": "/*" },
{ "not": { "selector_matches": "[data-no-prerender]" } }
] },
"eagerness": "moderate"
}]
}
</script>
<p>Soporte: <b id="support"></b></p>
<p><a href="destino.html">Ir al destino (se prerenderiza)</a></p>
<p><a href="destino.html?x" data-no-prerender>Ir al destino sin prerenderizar</a></p>
<script>
document.querySelector('#support').textContent =
HTMLScriptElement.supports?.('speculationrules') ? 'sí' : 'no (los enlaces cargan de forma normal)';
</script><!doctype html>
<meta charset="utf-8">
<title>Speculation Rules · ejemplo mínimo</title>
<!-- Prerenderiza los enlaces de esta web al pasar el ratón (200 ms) o al empezar a pulsar.
Chromium 109+. Sírvelo por HTTP (npx serve .). -->
<script type="speculationrules">
{
"prerender": [{
"source": "document",
"where": { "and": [
{ "href_matches": "/*" },
{ "not": { "selector_matches": "[data-no-prerender]" } }
] },
"eagerness": "moderate"
}]
}
</script>
<p>Soporte: <b id="support"></b></p>
<p><a href="destino.html">Ir al destino (se prerenderiza)</a></p>
<p><a href="destino.html?x" data-no-prerender>Ir al destino sin prerenderizar</a></p>
<script>
document.querySelector('#support').textContent =
HTMLScriptElement.supports?.('speculationrules') ? 'sí' : 'no (los enlaces cargan de forma normal)';
</script><!doctype html>
<meta charset="utf-8">
<title>Speculation Rules · destino</title>
<p id="out">…</p>
<p><a href="index.html">Volver</a></p>
<script>
const out = document.querySelector('#out');
const show = () => {
// activationStart > 0: la página ya estaba prerenderizada cuando la persona llegó
const nav = performance.getEntriesByType('navigation')[0];
out.textContent = nav?.activationStart > 0
? `Prerenderizada: se mostró ${Math.round(performance.now() - nav.activationStart)} ms después de activarse.`
: 'Carga normal (sin prerenderizar).';
};
// Mientras se prerenderiza, no cuentes visitas ni lances analítica
if (document.prerendering) document.addEventListener('prerenderingchange', show, { once: true });
else show();
</script><!doctype html>
<meta charset="utf-8">
<title>Speculation Rules · destino</title>
<p id="out">…</p>
<p><a href="index.html">Volver</a></p>
<script>
const out = document.querySelector('#out');
const show = () => {
// activationStart > 0: la página ya estaba prerenderizada cuando la persona llegó
const nav = performance.getEntriesByType('navigation')[0];
out.textContent = nav?.activationStart > 0
? `Prerenderizada: se mostró ${Math.round(performance.now() - nav.activationStart)} ms después de activarse.`
: 'Carga normal (sin prerenderizar).';
};
// Mientras se prerenderiza, no cuentes visitas ni lances analítica
if (document.prerendering) document.addEventListener('prerenderingchange', show, { once: true });
else show();
</script>What’s new
Keep in mind
- Never speculate on URLs that change state (log out, cart).
- Do not count visits while document.prerendering is true; wait for prerenderingchange.
- Inline rules need 'inline-speculation-rules' in the CSP; via the header, serve the JSON as application/speculationrules+json.
Further reading
Sources
- Chrome · Prerender pages23 Jan 2026
- MDN · Speculation Rules API10 Sep 2026
- Chrome 151 release notes28 Jul 2026
- MDN browser-compat-data 8.1.324 Sep 2026