Experiments / 11
Instant Navigation
How can the next page load before you click?
The problem
Everything so far has been about making a page load faster. There is a more radical option: make the load happen before the click. If the browser has already fetched, built and rendered the next page by the time you ask for it, the navigation is instant, whatever the network and the CPU are doing.
There are two separate tools. The back/forward cache makes going back instant by keeping the whole page in memory. Speculative loading makes going forward instant by guessing where you’re going. Both are gambles with different costs, and both fail for reasons that are easy to introduce by accident.
A session of ten navigations
Start with no speculation, then work down the presets. Watch two things: the bars, and the “wasted” numbers.
- Average navigation
- 526 ms
- Instant navigations
- 4 of 10
- Data wasted
- 0 KB
- CPU wasted
- 0 ms
- 1click →cold load849 msno speculation
- 2← backback/forward cache40 msrestored from memory
- 3click →cold load849 msno speculation
- 4click →cold load849 msno speculation
- 5← backback/forward cache40 msrestored from memory
- 6click →cold load849 msno speculation
- 7← backback/forward cache40 msrestored from memory
- 8click →cold load849 msno speculation
- 9click →cold load849 msno speculation
- 10← backback/forward cache40 msrestored from memory
Eligible. Leaving the page keeps it, JavaScript state and all, in memory.
A cold load of the next page takes 849 ms here. Across the session the average navigation is 526 ms, and 4 of 10 are instant (under 100 ms).
Back and forward are near-instant: the browser kept the whole page, JavaScript state included, in memory.
Session: six forward clicks, four back/forward moves. Hits are spread evenly. Prerender work = full load + render; prefetch = the document only.
Things to try
- “Prefetch on hover”. A modest, cheap win: only the document request is saved. The page still has to fetch its styles, scripts and images.
- “Prerender on hover”. At a typical 350 ms between resting on a link and clicking, a prerender only gets a partial head start. Raise “Pointer rests, then clicks after” to a second and a right guess becomes near-instant; drop it to 250 ms and it barely helps.
- “Prerender immediately”. The page is nearly always ready, but now it guesses three links at once, and most of the work is thrown away. Look at the data and CPU wasted.
- Lower the chance the guess is right. The savings shrink and the waste doesn’t.
- Add an
unloadhandler and watch the four back navigations go from instant to a reload. - Switch the CPU to Desktop and the network to 100 Mbps. A cold load is already fast, so there is much less to gain.
Going back: the back/forward cache
When a visitor leaves a page, the browser can keep the entire page in memory (the DOM, the JavaScript heap, scroll position, everything) and bring it back as it was when they press back or forward. It isn’t the HTTP cache, which stores files. It stores a live page. Restoring one takes a few tens of milliseconds, whatever the network is doing.
It’s on by default in the major browsers, and the thing to know is that pages quietly opt out. Common reasons:
- An
unloadevent handler. Desktop Chrome and Firefox treat it as disqualifying; Chrome on Android and Safari will still try to cache the page, but don’t rely on that. Usepagehide(andvisibilitychange) instead. Cache-Control: no-storeon the page. Browsers are loosening this, but don’t count on it.- An open connection (a WebSocket, WebRTC, a pending transaction) when the user leaves. Close it on
pagehide. - A page opened with
window.openthat keeps a reference to the window that opened it.
When a page is restored from the cache, load doesn’t fire again. Listen for pageshow and check
event.persisted if you need to refresh anything such as a stale session, a cart count or a timer.
Going forward: speculative loading
The modern way to speculate is the Speculation Rules API, a JSON block in the page:
<script type="speculationrules">
{
"prefetch": [{ "urls": ["/pricing"], "eagerness": "conservative" }],
"prerender": [{
"where": { "href_matches": "/articles/*" },
"eagerness": "moderate"
}]
}
</script>
- prefetch
- Fetches the next page’s document only. Cheap. It saves one request, and the page still loads its resources after the click.
- prerender
- Loads and renders the whole page in a hidden context, scripts and all. Activating it when the visitor clicks is close to instant. It costs the full load, whether or not the visitor goes there.
- eagerness
-
How readily to speculate.
conservativeacts on pointer-down,moderatewhen the pointer rests on a link (about 200 ms on desktop Chrome) or is pressed,eagerafter a very short hover on desktop, andimmediateas soon as the rules are seen. The more eager, the better the head start, and the more wrong guesses.
Support is currently in Chromium browsers. Others ignore the script block, so it’s a safe progressive enhancement: supporting browsers get instant navigations, the rest behave as before. Cross-origin speculation has extra privacy restrictions.
What can go wrong with prerendering
- Side effects. A prerendered page runs its JavaScript. Analytics fire for a page nobody visited, a “view” counter ticks, a one-time offer is consumed. Defer that work until the page is activated using
document.prerenderingand theprerenderingchangeevent. - Never speculate on URLs that change state. A link like
/logoutor/add-to-cart?id=3must not be prerendered or prefetched. Use rules that match only safe, read-only pages. - Cost. Data, CPU and battery, and load on your servers. Measure how often the guess is right before going eager.
- Server awareness. Speculative requests carry a
Sec-Purposeheader, so your server can recognise and, if needed, treat them differently.
One related tool, view transitions, makes navigation look smoother with an animated hand-off between pages. It doesn’t make the next page arrive any sooner, so treat it as polish.
Common misconceptions
“Just prerender everything and the whole site is instant.”
Every wrong guess is a full page load nobody asked for. The widget’s “immediately” preset shows the data and CPU that disappears. Speculate where intent is clear: the link under the pointer, the obvious next step.
“Prefetch and prerender are the same thing.”
Prefetch fetches the document. Prerender runs the page. They have very different gains and very different costs.
“The back/forward cache is automatic, so there’s nothing to do.”
It’s automatic until something disqualifies the page. One legacy unload handler (on desktop Chrome and Firefox), or one
forgotten connection, turns an instant back button into a full reload.
See it on a real page
- In DevTools, Application → Back/forward cache has a “Test back/forward cache” button that reports exactly why a page isn’t eligible.
- Application → Speculative loads shows which rules matched and whether each speculation was used or failed.
-
On a page, find out whether it was prerendered:
const nav = performance.getEntriesByType('navigation')[0]; console.log(nav.activationStart > 0 ? 'prerendered' : 'normal load'); - Count how often back navigations restore from the cache in the field, using
pageshowwithevent.persisted.
Model assumptions
What this simulation simplifies
- One kind of page: a 30 KB document and 250 KB of critical resources, then 80 ms of rendering on a desktop CPU (scaled by the device). A cold load is the document, then the resources, then render.
- The session is fixed: six forward clicks and four back/forward moves. Correct guesses are spread evenly through it.
- Prefetch gets as far as the document. Prerender does the whole load and render. In the model a prerender activates in about 35 ms and a back/forward cache restore takes about 40 ms; real times vary but are the same order of magnitude.
- Moderate eagerness starts 200 ms after the pointer rests; conservative acts about 90 ms before the click; the widget’s “Immediate” option starts several seconds early on three candidate links (Chrome’s own
eagerlevel sits between that and moderate). - Without the back/forward cache, going back costs a revalidation request and a render, with assets coming from the HTTP cache.
- Real browsers limit how many speculations run at once (Chrome allows 2 at a time for moderate and conservative, and up to 10 prerenders and 50 prefetches for immediate) and may decline them with Save-Data, Energy Saver, low memory, or when the visitor turned off “Preload pages”.