A <Link> starts loading its destination when it scrolls into view, not when you click. Loading the demo's home page in a production build issued one document request and then one prefetch per visible link:
14ms document / rsc=- prefetch=- 140ms rsc /?_rsc=5CB68i4pnAekjehf rsc=1 prefetch=1 141ms rsc /blog?_rsc=5CB68i4pnAekjehf rsc=1 prefetch=1 141ms rsc /blog/hello-world?_rsc=5CB... rsc=1 prefetch=1 141ms rsc /reports?_rsc=5CB68i4pnAe... rsc=1 prefetch=1
rsc: 1 asks for the component payload instead of HTML; next-router-prefetch: 1 marks the request as speculative, so a proxy or a log filter can tell a prefetch from a real navigation; _rsc is a cache buster keeping document and payload under separate CDN keys. Missing is /dashboard, whose link carries prefetch={false}: its request appeared only on the click.
At the default, prefetch={null} (also spelled "auto"), a static route is prefetched whole and a dynamic route is skipped — unless the segment has a loading.js, in which case Next.js 10,514 fetches everything down to that boundary: the layouts and the fallback, never the slow part. That is the real reason to add a loading.js to a dynamic route. prefetch={true} takes the whole route either way; prefetch={false} fetches nothing until the click. Prefetching runs in production only, so measure against next build && next start. On a long list the viewport rule can fire hundreds of requests; the cure is prefetch={active ? null : false} on a <Link> whose onMouseEnter flips active.
Partial prefetching
Next.js 16.3 (3 August 2026) shipped Instant Navigations, whose centerpiece is partial prefetching. It needs two flags, and does nothing without Cache Components (Caching and Revalidation):
const nextConfig = { cacheComponents: true, partialPrefetching: true }
export default nextConfigThe unit of prefetching then becomes the App Shell: everything in a route that does not depend on the URL — static markup, <Suspense> fallbacks, and any "use cache" result whose stale lifetime is at least five minutes. One shell per route serves every link pointing there, so a grid of 200 links to /store/[slug] prefetches one shell, not 200 pages. Session data from cookies() or headers() still lands in the shell; only params and searchParams are excluded.

With the flag on, prefetch={true} no longer means "fetch the whole page". It opts a link into per-link prefetching, resolving that link's URL data alongside the shell at one server invocation per prefetchable link — worth it for one or two destinations, not for a list. export const prefetch = 'partial' adopts the behavior one page at a time while the global flag is off.
In development the Next.js DevTools add a Navigation Inspector whose "Pause on navigations" toggle freezes the page on its loading state, and the dev overlay raises a non-blocking insight when a route reads params outside a <Suspense> boundary. For CI, @next/playwright exports an instant() helper that scopes assertions to the immediately available UI.