WritingNext.js Core Web Vitals Optimization Checklist: LCP, CLS, and INP — LinkIT
6 min readnextjs, core-web-vitals, web-performance, react

Next.js Core Web Vitals Optimization Checklist: LCP, CLS, and INP

Review LCP, CLS, and INP in Next.js, including image loading, fonts, scripts, component boundaries, and caching.

Next.js includes image, font, script-loading, and rendering tools that can affect Core Web Vitals. Measure LCP, CLS, and INP first, then use this checklist to investigate the components and resources behind poor results.

Before You Start: Measure First

Do not guess. Run your app through:

  • PageSpeed Insights with your production URL for field data from real users
  • Lighthouse in Chrome DevTools for lab data and specific recommendations
  • WebPageTest for waterfall analysis and filmstrip rendering

Field data (from CrUX, shown in PageSpeed Insights) represents real users on real devices. Lab data (Lighthouse) is a simulated load. Both matter. Fix field data failures first.

Next.js Core Web Vitals Checklist

LCP: Largest Contentful Paint

next/image for every image

The next/image component handles format conversion to WebP/AVIF, lazy loading by default, and serves the correct size via srcset. If you have raw img tags in your JSX, you are opting out of all of this.

Evaluate next/image for images that benefit from its sizing and optimization. For remote sources, configure narrowly scoped remotePatterns in next.config.js.

Load the LCP image promptly

The example below uses the older priority prop. Next.js 16 deprecates it in favor of preload; current guidance also recommends considering loading="eager" or fetchPriority="high" for the image. Choose based on the measured LCP resource. Next.js Image reference.

priority

Prioritize the measured LCP image rather than every image on the page.

Self-host fonts with next/font

next/font/google downloads fonts at build time and serves them with the application’s assets, avoiding browser requests to Google. Automatic fallback adjustment can reduce font-related layout shift; check the chosen font and configuration rather than assuming all shifts are eliminated.

Replace Google Fonts link tags in your _document.tsx or layout with next/font/google imports:

import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'], display: 'swap' });

Apply the font className to your root layout element.

Verify TTFB with React Server Components

App Router Server Components render on the server and stream HTML to the client. This reduces JavaScript bundle size (Server Components have zero client-side JavaScript) and can improve TTFB. Move data-fetching logic into Server Components and keep Client Components for interactive UI only.

Enable Incremental Static Regeneration or static rendering where possible

Statically rendered pages are served from CDN cache. TTFB for a cached static page is typically under 50ms. Dynamic server rendering adds server computation time. Use generateStaticParams and static rendering for pages where content does not change per-request.

CLS: Cumulative Layout Shift

Always set width and height on next/image

Next.js Image component requires width and height props (or fill layout). These are used to reserve space before the image loads. If you get a TypeScript error for missing dimensions, that is the framework protecting you from CLS.

For images with unknown dimensions at build time, use fill with a positioned container that has an explicit aspect ratio set via CSS.

Do not use dynamic content that inserts above existing content

Cookie banners, notification bars, and chat widgets that render after hydration are common CLS sources in Next.js apps. Options:

  • Render them server-side so they are part of the initial HTML
  • Reserve their height with a placeholder before they appear
  • Use position: fixed so they do not affect document flow

Check for hydration-related layout shifts

If a component renders differently on the server versus the client — for example, because it reads from localStorage or a browser API — Next.js will hydrate it and the DOM difference can cause a layout shift. Use suppressHydrationWarning only when truly necessary, and prefer conditional rendering patterns that produce matching server and client output.

Verify font fallback metrics

Check whether automatic fallback adjustment is enabled for the selected font. Inspect generated CSS and measure layout shift while fonts load. See the font reference.

INP: Interaction to Next Paint

Minimise Client Components

Every Client Component adds JavaScript to the bundle that executes on the main thread. Large client bundles lead to long parse-and-compile tasks that block the main thread and inflate input delay.

Audit Client Components for code that can run on the server, then remeasure the interaction. Bundle reduction may help, but the profile determines whether it addresses the INP bottleneck.

Code-split with dynamic imports

Use next/dynamic for heavy Client Components that are not needed on the initial render — modal dialogs, rich text editors, chart libraries, complex forms. Dynamic imports split those components into separate chunks that are only loaded when needed.

const RichEditor = dynamic(() => import('./RichEditor'), { ssr: false });

Audit third-party scripts with next/script

The next/script component accepts a strategy prop:

  • beforeInteractive: loads before Next.js code and hydration; execution does not block hydration. Reserve it for site-wide scripts that need this early loading. Next.js Script reference.
  • afterInteractive (default): loads after hydration. Appropriate for tag managers and analytics.
  • lazyOnload: loads during browser idle time. Good for chat widgets, low-priority scripts.

Move every third-party script from a raw script tag to next/script with the appropriate strategy. Most marketing tags belong in afterInteractive or lazyOnload.

Profile interactions with Chrome DevTools

Record a performance trace while clicking your most-used interactive elements. Look for input delay caused by long tasks. Common Next.js-specific culprits: large React re-renders triggered by global state updates, heavy useEffect chains on user interaction, and unoptimised data mutations that re-render large component trees.

Use React DevTools Profiler to identify which components are causing slow re-renders.

Caching and Infrastructure

  • Static assets (JS, CSS, images) served with Cache-Control: public, max-age=31536000, immutable
  • Cache eligible public HTML with a deliberate TTL or ISR policy; do not share user-specific responses between users
  • Deployed on a CDN-first platform (Vercel, Cloudflare Pages, or equivalent)
  • Core Web Vitals monitored in production with a RUM tool or Search Console

Validate and Iterate

After applying fixes, redeploy and re-run PageSpeed Insights. Allow 28 days for CrUX field data to update — it reflects a rolling 28-day window of real user data.

Use Lighthouse CI to check for performance regressions in pull requests. Compare those lab checks with production field data.

Discuss a Next.js performance review with LinkIT.