Skip to the content.

Prefetching hides latency by doing the work before it's asked for

Prefetching hides latency by doing the work before it's asked for

The fastest network request is the one that already finished before the user asked for it. Prefetching is doing exactly that: predicting what the user is likely to do next — the route they will click, the data the next screen needs — and loading it during idle time, so when the click comes, the answer is already in cache and the transition is instant. It does not make the network faster; it hides the latency by moving it earlier, into a moment when the user is not waiting. Done well it is the difference between an app that feels snappy and one that shows a spinner on every navigation, even though the underlying requests are identical.

Prefetching moves the fetch into idle time so the click resolves instantly Without prefetch: click then wait for the fetch. With prefetch: the fetch happens on hover/idle before the click, so the click is instant. no prefetch click wait for fetch (spinner) prefetch fetch on hover/idle click instant ✓ latency hidden before the click
Prefetching relocates the fetch to idle time before the click. The work is the same; the user just never waits for it, because it finished earlier.

Prefetch on intent signals

The trick is predicting the next action from a cheap signal. Hover over a link is a strong one — the user is deciding to click — and gives you a few hundred milliseconds of head start. So warm the next route’s code and data on hover:

// hover is intent: warm the next route's chunk + data before the click lands
link.addEventListener("mouseenter", () => {
  import("./routes/Product.jsx");                 // code chunk
  queryClient.prefetchQuery(["product", id], () => fetchProduct(id));  // data
}, { once: true });

By the time the click fires, both the code and the data are cached, and the navigation renders immediately.

Use the browser’s declarative hints

You do not always need JavaScript — the platform has declarative prefetch hints the browser schedules intelligently during idle time. <link rel="prefetch"> fetches a resource for a likely future navigation at low priority; preconnect warms the connection to an origin you will call:

<!-- likely-next resources, fetched at idle/low priority by the browser -->
<link rel="prefetch" href="/product.js" as="script">
<link rel="preconnect" href="https://api.example.com">   <!-- warm the connection early -->

Many frameworks do route-level prefetch automatically — a <Link> in view or hovered gets its chunk prefetched without you writing anything.

Prefetch the likely, not everything

The discipline is that prefetching spends resources on a guess, so a bad guess is waste — data the user never needed, bandwidth burned on a mobile plan, cache thrashed. So prefetch what is likely, not everything: the hovered link, the next page of a list the user is scrolling, the step-two data while they fill step one. Respect the user’s constraints — honour navigator.connection.saveData and back off on slow connections — and prefer idle time (requestIdleCallback, low-priority hints) so prefetching never competes with what the user is actively waiting on. Used with judgement, it is one of the highest-impact perceived-speed tools available, because it attacks latency where the user cannot feel the cost. The infinite-scroll-list exercise is a natural home for it — prefetch the next page before the user reaches the bottom — and render-strategy-choice is where prefetch fits into the larger loading plan.