Speculation Rules API
Prefetch and prerender entire pages before the user navigates using the Speculation Rules API making navigations feel instant.
Overview
Key Points
- •Defined in `<script type="speculationrules">` containing JSON no src attribute, inline only
- •`"prefetch"` downloads the page HTML in the background without rendering
- •`"prerender"` fully renders the page in a hidden tab; navigation is instant (zero loading time)
- •`"eagerness"` controls timing: "immediate", "eager", "moderate" (hover ~200ms), "conservative" (click)
- •`"where": { "href_matches": "..." }` match URLs with a pattern
- •`"where": { "selector_matches": "a.prerender-me" }` match links by CSS selector
- •`"where": { "and": [...] }` combine conditions with and/or/not logic
Examples
Basic speculation rules for prefetch and prerender
<!-- Prerender next page links when hovered (moderate eagerness) -->
<script type="speculationrules">
{
"prerender": [
{
"where": {
"href_matches": "/products/*"
},
"eagerness": "moderate"
}
],
"prefetch": [
{
"where": {
"href_matches": "/blog/*"
},
"eagerness": "conservative"
}
]
}
</script>"moderate" triggers on hover (after ~200ms); "conservative" triggers on mousedown/pointerdown.
Targeting links by CSS selector and excluding URLs
<script type="speculationrules">
{
"prerender": [
{
"where": {
"and": [
{ "selector_matches": "a[data-prerender]" },
{ "not": { "href_matches": "/admin/*" } },
{ "not": { "href_matches": "/*?*" } }
]
},
"eagerness": "moderate"
}
]
}
</script>
<!-- Mark specific links for prerendering -->
<a href="/dashboard" data-prerender>Dashboard</a>
<a href="/profile" data-prerender>Profile</a>
<a href="/admin/settings">Settings</a> <!-- excluded -->Combining and/not conditions prevents prerendering admin pages, authenticated routes, or parameterized URLs.
Programmatic speculation rules via JavaScript
<script>
// Add speculation rules dynamically after determining user intent
function addPrerender(url) {
const existing = document.querySelector('script[type="speculationrules"]');
const rules = existing ? JSON.parse(existing.textContent) : { prerender: [] };
rules.prerender.push({
urls: [url],
eagerness: 'immediate'
});
if (existing) {
existing.textContent = JSON.stringify(rules);
} else {
const script = document.createElement('script');
script.type = 'speculationrules';
script.textContent = JSON.stringify(rules);
document.head.appendChild(script);
}
}
// Example: prerender next page when user has been on page for 3 seconds
setTimeout(() => addPrerender('/next-lesson'), 3000);
</script>Dynamic rules allow intelligent prerendering based on user behavior analytics.
Common Mistakes
- Prerendering authenticated or personalized pages if prerendering runs before auth is confirmed, it may show incorrect state
- Setting eagerness="immediate" on many URLs simultaneously this wastes bandwidth for users who never navigate to those pages
- Using speculation rules without testing that prerendered pages handle being shown after background render correctly (e.g., analytics double-counting)
Note
Test speculation rules in Chrome DevTools > Application > Speculation Rules (or > Background Services > Speculative Loads). It shows which rules matched, which URLs were prefetched/prerendered, and the current status.
Exercises
Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4