CDN Requests
A visitor lands on Saturday's homepage. They look at the hero, scroll through the grid, click "Drops," and leave. They were on the site for fourteen seconds. In those fourteen seconds, their browser made requests for:
- The homepage HTML
- Five product page bundles, one for each card in the grid
- The drops page bundle, the only one they actually clicked
Three of those product prefetches were pure speculation. They went to product pages the visitor never looked at. Multiply that by a hundred thousand visitors on drop day and you've burned a meaningful chunk of your CDN request budget fetching route bundles for pages nobody opens.
Next.js prefetches aggressively by default because it makes the next click feel instant. For high-engagement apps that's the right call. For an e-commerce browse where most visitors look at one or two things and bounce, it's worth tuning.
Outcome
Product card links and drop schedule rows fetch their route bundles only when the visitor clicks, not when the cards scroll into the viewport. Saturday's CDN request count for repeat browsing drops noticeably.
Hands-on exercise 4.2
Two files. One prop on each high-volume link.
Requirements:
- Open
app/page.tsx - Find the product grid (
liveProducts.map(...)). Each item renders a<Link>wrapping the product card. - Add
prefetch={false}to that<Link> - Open
app/drops/page.tsx - Find the schedule rows (
sortedDrops.map(...)). Same pattern. - Add
prefetch={false}to that<Link> - Leave
app/layout.tsxuntouched. The nav has two links: Shop and Drops. Prefetching those is cheap and the UX win is real. - Commit, push, redeploy
Implementation hints:
prefetch={false}doesn't disable navigation. The link still works on click. It removes the speculative background fetch entirely, on viewport entry and on hover both.- That means the route bundle downloads at click time. For pages the size of Saturday's product pages, that's tens of milliseconds on a decent connection, a fair price for skipping the four fetches the visitor never asked for.
- For the nav (
<Link href="/" />,<Link href="/drops" />), keep prefetching on. Those bundles are loaded by almost every user and the prefetch behavior is a clear UX win. - If you want to be more aggressive, set
prefetch={false}on the "View Details" button under the hero teaser as well. That's a single high-value link, so prefetching is defensible there. Pick your battles.
Try It
After the redeploy, open Saturday's homepage in DevTools. Filter the Network panel to "Doc" or "Fetch."
Before the change, scrolling the homepage would trigger a flurry of fetches as the product grid entered view. You'd see five additional RSC (React Server Component) requests appear in the panel.
After the change, the same scroll should be quiet. The Network panel stays at whatever it was after the initial load. Hovering doesn't trigger anything either; with prefetch={false}, Next.js skips speculation entirely. Click a product card and you'll see one RSC request appear at navigation time, for the one page the visitor actually opened.
In the Vercel dashboard, open Saturday > Observability > Edge Requests. The shape you're looking for is the ratio of requests-per-page-view. Before the change, a single homepage view triggers 1 (the page) + 5 (the grid prefetches) + however many static asset fetches. After, it's 1 + 0 + the assets. On a busy day this difference is the difference between staying within your tier and exceeding it.
Done-When
- Product grid
<Link>inapp/page.tsxhasprefetch={false} - Drop schedule
<Link>inapp/drops/page.tsxhasprefetch={false} - Nav
<Link>elements inapp/layout.tsxare unchanged - DevTools shows no RSC fetches when scrolling the homepage
- Clicking a product card triggers exactly one RSC fetch, at navigation time
Troubleshooting
Navigation feels slower now.
That's the honest tradeoff: without prefetching, the route bundle downloads when the visitor clicks. On Saturday's small pages the delay should be barely perceptible. If it's genuinely laggy, the bundle is big enough that click-time fetching hurts, and that's a route bundle size problem worth investigating with next build --analyze before you reach for prefetching to paper over it.
Edge Requests didn't drop in the dashboard.
Give it 24 hours. The change only shows up in aggregate traffic, and you need a meaningful sample before the trend is visible. Also check that the homepage and drops page got new deployments after your push, the old behavior persists for any pre-existing client-rendered page.
I'm seeing RSC fetches I don't expect.
Some are from Next.js's own internal navigation. The dev tools panel can be noisy. Filter by your domain to remove third-party requests, and look at the path of each RSC request to confirm it's actually a product page rather than something internal.
Solution
The product grid change:
{liveProducts.map((product) => (
<Link
key={product.id}
href={`/products/${product.slug}`}
prefetch={false}
className="group"
>
{/* ...card contents */}
</Link>
))}The drops row change:
<Link
key={drop.id}
href={`/products/${product.slug}`}
prefetch={false}
className="grid grid-cols-[120px_1fr_120px_100px] gap-6 py-6 border-b border-line items-center hover:bg-white transition-colors"
>
{/* ...row contents */}
</Link>Prefetching is a UX win when it matches user behavior. On a marketing site where people browse five things and click one, viewport prefetching loses the bet on the four they didn't click. Turning it off means the one click pays for one fetch, and the four misses cost nothing.
Up next: builds. Specifically, the builds you didn't need to run.
Was this helpful?