Back

Hitting Green Core Web Vitals in Next.js

A practical guide to LCP, INP and CLS in Next.js — image handling, font loading, the App Router boundary, and costly third-party scripts.

Hitting Green Core Web Vitals in Next.js
Written by
BSH Technologies
Published on2026-06-10

Green Core Web Vitals start with knowing which metric is failing

Core Web Vitals in Next.js come down to three numbers: Largest Contentful Paint, Interaction to Next Paint, and Cumulative Layout Shift. Before changing anything, find out which one is actually red. Optimising LCP when your problem is INP is wasted effort, and the fixes are completely different. Pull field data from the Chrome User Experience Report or your real-user monitoring rather than trusting a single lab run — lab numbers on a fast machine hide the problems your users on mid-range phones over patchy mobile networks actually feel. The whole point of starting with measurement is that it tells you where the time is going, so you spend your effort on the bottleneck instead of the part of the page that was already fast.

LCP: the hero image and the font are usually the culprits

Largest Contentful Paint is almost always your hero image or your headline text. Next.js gives you the tools, but the defaults will not save you if you ignore them.

  • Use the Next.js Image component with explicit width and height so the browser reserves space and never reflows.
  • Mark the single above-the-fold hero image with priority so it is preloaded instead of lazy-loaded.
  • Serve AVIF or WebP and never ship a 2000px source into a 600px slot.
  • Load fonts through next/font so the font file is self-hosted, preloaded, and the layout does not shift when it swaps in.

A headline that renders in a fallback font and then jumps when the web font arrives hurts both LCP and CLS at once. next/font with a sensible fallback removes that jump. One more trap worth naming: only the hero gets priority. If you mark several images as high priority, you have effectively prioritised nothing, because the browser now races them all against each other for the same bandwidth. Reserve that signal for the single largest element above the fold.

INP: ship less JavaScript to the client

Interaction to Next Paint measures how quickly the page responds when a user taps or clicks. In the App Router, the biggest lever is the server-versus-client boundary. Every component you mark with the client directive ships its JavaScript to the browser and competes for the main thread.

  • Keep components on the server by default; only reach for the client directive where you genuinely need state, effects, or browser APIs.
  • Push the client boundary as far down the tree as you can — a single interactive button does not require its whole parent section to be a client component.
  • Dynamically import heavy widgets — rich editors, charting libraries, maps — so they load on demand instead of blocking first interaction.

The instinct to make a large parent interactive just to handle one small control is what quietly inflates a bundle. Isolate the interactive leaf instead. A common pattern is a mostly static page with one small island of interactivity; keep that page a server component and make only the island a client component, passing data down as props. The result is a page that paints fast and stays responsive because the browser is not parsing and executing a wall of JavaScript before it can react to a tap.

CLS: reserve space for everything that arrives late

Layout shift comes from content that loads after first paint and pushes the rest of the page around. The fix is always the same: reserve the space up front.

  • Give images and embeds explicit dimensions or an aspect-ratio box.
  • Never inject banners or cookie notices above existing content without holding space for them.
  • Render skeletons that match the final size of async content so the swap is invisible.
  • Be careful with web fonts whose metrics differ wildly from the fallback, since the reflow on swap registers as a shift.

Most layout shift is preventable with a little forethought about what loads late, and it is one of the easier vitals to drive to green once you start looking for the culprits deliberately.

Third-party scripts are the tax you forget about

Analytics, chat widgets, tag managers, and ad pixels routinely undo careful first-party work. Load them with the Next.js Script component using the afterInteractive or lazyOnload strategy so they never block the main thread during the moments that matter. Audit them periodically — teams add a tag for a campaign and never remove it, and three forgotten scripts can cost you more INP than all your own code combined. Each one runs on your users' devices whether or not anyone still looks at the data it sends, so a quarterly cull of dead tags is one of the highest-leverage performance tasks there is.

Measure in the field, not just the lab

Lab tools like Lighthouse are useful for catching regressions in CI, but they run on idealised conditions. Your real score is whatever your users on real devices experience, and Google ranks on field data, not lab data. Keep a field-data feedback loop running so you optimise for reality, not for a green number on a developer laptop. The gap between a fast laptop on office fibre and a mid-range phone on mobile data is enormous, and the field is the only place that gap shows up honestly.

How BSH can help

BSH Technologies builds Next.js applications with performance treated as a feature, not a cleanup pass before launch. If your vitals are stuck in the amber or red and you are not sure which change will move the needle, we can profile your app against real field data, fix the boundary and asset issues, and set up monitoring so the gains hold over time. Get in touch and we will start with a measurement, not a guess.

From the blog

View all posts
Designing Multi-Tenant SaaS That Scales
Software Dev

Designing Multi-Tenant SaaS That Scales

Choosing an isolation model, keeping tenant data separate, and dodging the noisy-neighbour and migration traps that bite SaaS later.

BSH Technologies
BSH Technologies · 2026-06-14
Infrastructure as Code: A Terraform Starting Point
Software Dev

Infrastructure as Code: A Terraform Starting Point

A grounded intro to Terraform — remote state, modules, plan-before-apply discipline, and the early mistakes that turn IaC into a liability.

BSH Technologies
BSH Technologies · 2026-06-06