Most Next.js Core Web Vitals guides read like a compliance checklist. This isn't that.
After shipping a dozen App Router projects into production, four levers consistently move the numbers. Everything else is noise.
1. LCP: the image is usually the bottleneck
If your Largest Contentful Paint is above 2.5s, 90% of the time it's your hero image. Two things fix it:
Priority load + fetchpriority. Next.js <Image> supports priority — use it on your hero image. Not two, not three. One.
AVIF over WebP. The Next.js image optimiser serves AVIF by default when the browser supports it. Confirm your build isn't overriding this in next.config.js.
2. INP: hydration is the killer
Interaction to Next Paint measures how long a click blocks before painting the next frame. In App Router, hydration is usually the culprit. Two moves:
- Push interactive components as deep into the tree as possible so hydration is scoped, not sitewide.
- Use
useTransitionfor state updates that trigger re-renders under 100ms.
3. CLS: reserve the space
Layout shift almost always comes from images without explicit dimensions, or web fonts loaded without font-display: swap. Next.js <Image> requires width/height (or fill with a sized parent). Web fonts loaded through next/font handle FOUT correctly by default.
4. Ship measurement, not opinions
Real User Monitoring beats synthetic benchmarks. Install @vercel/speed-insights (or the equivalent) and look at 75th percentile CWV, not median. Median hides the tail — 75th shows what your users actually feel.
The ones that waste your time
- Aggressive route prefetching. Turn it off for infrequently-clicked links; it burns bandwidth without changing perceived speed.
- Manual code-splitting. The Next.js compiler is smarter than most manual splits — leave it alone unless you've measured the bundle.
- Preloading fonts you rarely use. Preload display fonts if you use them above the fold, ignore body fonts.
Done right, greenfield Next.js should hit green Core Web Vitals before you write your first feature.