Prefetch strategy
Fetch on intent, not on scroll
Next prefetches a link when it enters the viewport. That default is right for a page with a handful of links and wrong for a grid of 405 cards, every one of them pointing at a route that is rendered on demand: the reader pays for cards they scrolled past. The catalog now opts those cards out and fetches when a pointer or the keyboard rests on one.
Measured from the completed build on 2026-09-12 06:33 UTC (build eoCv1yhT4VQe) by npm run build && npm run measure, and committed as docs/build-report.json. The numbers are not re-derived while the page renders, because a build cannot see its own output.
Cards that opt out
405
289 components + 116 prompts
One prefetch costs
15.8 KB
RSC payload for /components/tilt-card
A full page costs
93 KB
same URL, rendered without JavaScript
Grid scrolled end to end
6399 KB
if every card prefetched itself
What changed
- prefetch={false}The card link no longer fetches when it scrolls into view. Nothing is requested until the reader shows interest, which is the whole point: a scroll is not a destination.
- pointerenter · focusEither one arms a timer — a cursor sweeping across the grid never fires it, because the timer is cleared on pointer-leave and blur. Keyboard users get the same behaviour, which a hover-only version would not.
- 140 ms dwell → router.prefetch(href)Then the route is fetched exactly as the framework would have fetched it, so the click still lands on a warm payload.
The state is written to the DOM as data-intent-prefetch="idle|dwell|sent", so the behaviour can be checked in a browser without reading the component.
How the probe was taken
curl -s -H "RSC: 1" \ "/components/tilt-card?_rsc=x" | wc -c # 16179 bytes curl -s "/components/tilt-card" | wc -c # 95232 bytes
The RSC payload is what a prefetch downloads; the HTML figure is what a full navigation without JavaScript would fetch. Both were taken against this build's production server, not a development one, because the development payload carries a great deal that never ships.
What this does not measure
- The 6399 KB figure is arithmetic, not a trace: it multiplies the one payload we measured by the number of cards. Real scroll behaviour depends on viewport size and how far someone scrolls, which is why the page states the rule rather than a saving.
- Intent prefetching still fetches for a reader who hovers and then leaves without clicking. The trade is deliberate — one wasted request for an interested reader beats one per card for an uninterested one.
- The 2 intent links cover the component and prompt grids. Background cards carry no link at all in this build, so they are not part of the count.
Documents measured
498
prerendered pages in this build
Static prefetch links
0
`<link rel="prefetch">` across the build
Budget
0
no document may emit one
The build emits no static prefetch links
Next prefetches on intent at runtime, in script — it does not write <link rel="prefetch"> into the prerendered HTML. That zero is the budget: the distribution across all built documents is published here, and the export harness fails a build that starts emitting static prefetch links, because a document with twenty of them is a reader paying for twenty routes they never opened.
Measured 0 prefetch links on 0 of 498 documents; the most any single page carries is 0. The intentional prefetching this site does — the intent link on catalog cards — is scripted and is the subject of the page above, not of this count.