Next.js hands you everything you need to score 100 on Core Web Vitals. It also lets you tank it just as easily. The score comes from using the tools on purpose — here's the exact code.
LCP: your hero image is the culprit
The Largest Contentful Paint element is almost always the hero. Two moves fix most of it — server-render it (default in the App Router) and prioritise the image:
import Image from 'next/image'
<Image src="/hero.webp" alt="Dashboard" width={1200} height={600} priority />
The priority flag preloads it so it paints first instead of waiting on JavaScript.
INP: ship less JavaScript
Interaction to Next Paint is the metric most sites now fail, and the cause is always too much client JS on the main thread. Keep components as Server Components by default, and code-split the heavy ones:
import dynamic from 'next/dynamic'
const Chart = dynamic(() => import('./Chart'), { ssr:false })
CLS: reserve the space
Layout shift comes from elements with no reserved space — unsized images and swapping fonts. next/font self-hosts and kills the font shift; always pass width/height to images.
Measure where it counts
Lab scores flatter you. Validate on real mobile in the Core Web Vitals report (field data), not just desktop Lighthouse.
Want a 100/100 starting point? I build Next.js templates with these optimizations baked in.
What's blocking your score right now — LCP, INP, or CLS? 👇
Top comments (0)