LCP: get the main content on screen fast
The largest element above the fold — usually a hero image or headline — should render from the server with no client round-trip. Fetch its data on the server, avoid a client-side spinner-then-content dance for the hero, and preload the LCP image. The killer anti-pattern is fetching above-the-fold data in a useEffect: the user watches an empty box while the browser does a second trip.
// Prioritize the LCP image; size it so nothing shifts
import Image from 'next/image';
<Image src={hero} alt="" priority sizes="100vw"
width={1600} height={900} /> // priority preloads it; dims reserve spaceCLS: reserve space for everything
- Always give images explicit width/height or an aspect-ratio so the layout doesn't jump when they load.
- Reserve space for ads, embeds and late-loading banners with a min-height placeholder.
- Load fonts with next/font and a size-adjusted fallback so text doesn't reflow when the webfont swaps in.
- Never insert content above existing content after load — it shoves everything down and spikes CLS.
INP: keep the main thread free
Interaction latency comes from shipping too much JavaScript. In the App Router the win is architectural: server components by default, and 'use client' only on the leaves that truly need interactivity. That keeps the hydrated bundle small, so taps and clicks respond immediately instead of waiting behind a long JS task.
The most common Next.js performance mistake isn't a missing optimization — it's making everything a client component. Server-first is the single biggest lever on both bundle size and INP.
Bundle discipline
Audit what you ship: a bundle analyzer surfaces the heavy third-party dependency doing little (a giant date or icon library, a charting lib pulled in eagerly). Dynamically import below-the-fold and rarely-used components so they're not in the initial payload, and prefer lighter alternatives for the big offenders. Every kilobyte the browser parses is INP you're spending.
Measure real users, not just the lab
Lighthouse is a lab signal; Google ranks on field data (CrUX) from real devices and networks. Wire up real-user monitoring so you see p75 LCP/CLS/INP for actual visitors — the lab can look green while users on mid-range phones suffer. Optimize for the field numbers.
Key takeaways
- LCP: render the hero server-side and preload its image; never fetch above-the-fold data client-side.
- CLS: reserve space for images, embeds and fonts so nothing jumps.
- INP: server components by default, 'use client' only at interactive leaves.
- Trim the bundle with analysis + dynamic imports, and optimize against real-user field data.