BitlystBitlyst
PostsSeriesTopicsAbout

Bitlyst

Short, diagram-first posts on JavaScript, React & Next.js. Follow via RSS or the newsletter.

© 2026 Mohsen Fallahnejad

PostsSeriesTopicsLinksAboutRSSGitHubX
Posts/nextjs
Intermediate4 min read

Part 3 of Next.js Field Notes

Scrolling My Blog Index Prefetched All 51 Posts. One Config Line Cut It to 90 kB.

What a long list of Next.js <Link>s really costs in prefetch requests, measured on this blog, and how partialPrefetching shares one App Shell per route instead.

Mohsen Fallahnejad

Mohsen Fallahnejad

Nov 3, 2026Discuss

The /blog page on this site is one long list: 51 posts, 51 <Link>s. I opened it in a production build, scrolled from top to bottom without clicking anything, and counted the network requests:

code
RSC requests:        83
posts prefetched:    51 of 51
transferred:         576 kB  (gzip; 2.7 MB once decoded)

Every single post got downloaded in the background because its link scrolled past. Nobody reads 51 posts in a row. That's over half a megabyte spent on pages that, for almost every visitor, will never be opened.


The wrong turn: prefetch= everywhere#

The standard advice, which an older post on this blog repeats, is to switch prefetching off for big lists:

tsx
<Link href={`/${post.slug}`} prefetch={false}>
  {post.title}
</Link>

That fixes the bandwidth, but it trades it for something worse. Without a prefetch, the router has nothing for that route when you click. Next.js has to fetch it on click before it can show anything, so the instant navigation that made you pick <Link> in the first place is gone.

And it's aimed at the wrong thing. The cost isn't "too many links." It's that each link fetched its own full page.


The reveal: per-link prefetches vs. one shell per route#

There are two prefetching models in Next.js 16, and the difference is what gets downloaded per link.

What each Link prefetches as it scrolls into view

  1. 1

    Link enters viewport

    Next.js queues a prefetch, putting links in the viewport first

  2. 2

    Is prefetching per link, or per route?

    This is what partialPrefetching switches

then it depends
Per link (old default)
  1. ↳Fetch this post's full page
  2. →51 links = 51 full posts
Per route (partialPrefetching)
  1. ↳Fetch the /blog/[...slug] App Shell once
  2. →Every other post link reuses it
All 51 links point at the same route, /blog/[...slug]. With partial prefetching they share one download.

The old default prefetches each link's page in full. The docs put it plainly: "a page with N links to N routes produced ~N route prefetches."

With partialPrefetching: true, a link prefetches the route's App Shell instead: the parts of the page that don't depend on which post it is, like the layouts and loading states. Every link to /blog/[...slug] shares that one shell, fetched when the first of them shows up. The post itself, the part that depends on the URL, loads after you click.

It needs Cache Components, which this blog already uses, so turning it on was one line:

js
// next.config.mjs
const nextConfig = {
  cacheComponents: true,
  partialPrefetching: true,
};

The fix, measured#

Same page, same scroll, same browser, with the only change being that config value:

Scroll /blog top to bottomPer link (false)Per route (true)
RSC requests8324
Post routes prefetched51 of 51shell + 4 small route-tree requests
Transferred (gzip)576 kB90 kB
Decoded size2.7 MB330 kB

That's 84% less data, and the number stops growing with the list. With partial prefetching, the count was 23 requests before scrolling and 24 after. Add another hundred posts and it stays about the same, because they're all the same route.

The cost moved to the click, where it belongs. I navigated to the very last post on the list, one that never had its own prefetch. The router made one request for the post itself: 7.7 kB.

How I measured, so you can check it: next build then next start (Next.js 16.4.3), headless Chromium at 1440×900, a fresh browser per run, scrolling 400 px every 250 ms and waiting 3 seconds at the bottom. I counted every resource with _rsc= in its URL, using the Resource Timing API's transferSize. Three runs per setting gave identical numbers. It ran on localhost, so I'm not quoting request timings. On a real network, the click-time request adds one round trip before the post text appears.

Long lists of links aren't expensive. Prefetching each link's full page is.


The rule#

Before you reach for prefetch={false} on a long list, check whether those links share a route. If they do, and you're on Cache Components, turn on partialPrefetching and let them share one shell. Save prefetch={false} for links that really shouldn't be warmed up at all.


TL;DR#

  • Without partial prefetching, every <Link> that scrolls into view prefetches its own page, so a list of 51 posts downloaded 576 kB of pages nobody clicked.
  • prefetch={false} saves the bandwidth but makes every click wait for the server.
  • partialPrefetching: true (requires cacheComponents) prefetches one App Shell per route, shared by every link to it.
  • On this blog's index that cut the scroll from 83 requests and 576 kB to 24 requests and 90 kB, and the count no longer grows with the list.
  • The URL-specific part loads after the click instead; for one post that was a single 7.7 kB request.
Filed under#nextjs#link#prefetch#performance#app-router

Series

Next.js Field Notes

Tricks and traps from shipping Next.js App Router apps in production.

  1. 1One Line in My Root Layout Turned 158 Static Pages Into 0
  2. 2The Server Action That Ran Twice, but Only on localhost
  3. 3Scrolling My Blog Index Prefetched All 51 Posts. One Config Line Cut It to 90 kB. · you are here
  4. 4The redirect() that a try/catch swallowed · coming soon
  5. 5The useSearchParams call that blanked half the page in the HTML · coming soon

New parts land here first — subscribe below to get them in your inbox.

Was this worth your time?

One tap tells me what to write more of.

Mohsen Fallahnejad

Written by

Mohsen Fallahnejad

Frontend engineer explaining JavaScript, React & Next.js internals in posts you can finish over coffee. More about me

X ↗GitHub ↗LinkedIn ↗
← OlderThe Server Action That Ran Twice, but Only on localhostNewer →The Leaderboard Arrived Sorted. JavaScript Re-sorted It by User ID.

Start the discussion

Questions, corrections, a better approach? I read and reply to every comment.

Read next

Prefetching in Next.js Links Explained

How Next.js prefetch works in <Link>, why it makes navigation fast, and when to disable it.

Oct 4, 20252 min read

One Line in My Root Layout Turned 158 Static Pages Into 0

Reading a cookie with cookies() in the Next.js root layout opts every route out of static rendering. What it cost this blog, measured, and how to fix a theme flash without it.

Nov 1, 20267 min read3

Next.js App Router Caching — Cheat Sheet

A compressed reference of Next.js caching layers and controls (App Router).

Sep 20, 20252 min read

Get new posts in your inbox

No spam. Unsubscribe any time.

4 min left
Discuss👍 React
👍