Skip to main content
友田 陽大
Frontend
Next.js
パフォーマンス
フロントエンド
React
アクセシビリティ
アーキテクチャ設計

Core Web Vitals optimization guide [2026 edition] — improving INP, LCP, and CLS with Next.js to grow SEO and CV

A practical guide to improving 2026's Core Web Vitals (INP, LCP, CLS) with Next.js. With real code, it explains production-quality techniques: crushing the most failure-prone INP, image/font/SSR optimization to get LCP under 2.5 seconds, dimension reservation to bring CLS near zero, and field measurement (web-vitals / RUM).

Published
Reading time
8 min read
Author
友田 陽大
Share

"Fast" isn't an impression but measurable quality. This article, premised on Next.js, shows the procedure to improve with numbers, not guesses.


1. The true identity of the 3 metrics and the passing line

MetricWhat it measuresGoodNeeds improvementPoor
LCP (Largest Contentful Paint)Display speed of the main contentUnder 2.5 sec2.5–4.0 secOver 4.0 sec
INP (Interaction to Next Paint)Responsiveness to operationsUnder 200ms200–500msOver 500ms
CLS (Cumulative Layout Shift)Layout shiftUnder 0.10.1–0.25Over 0.25

There are two important premises.

  • Judged at the 75th percentile. Unless "75% of visits are good," you don't pass. Even if the average is good, slow-device and slow-network users dragging it down make it fail.
  • Field data is decisive. What Google looks at is real users (CrUX). Even if your local Lighthouse score is 100, if the field is slow, it fails.

2. Crush INP (2026, most important)

INP measures the delay "from a click/tap/key input until the screen next paints." The cause is almost always JavaScript blocking the main thread.

2-1. Don't send JS in the first place (the biggest lever)

React Server Components (RSC) don't send JS for processing that completes on the server to the client. Confining "use client" to the smallest leaf is the heart of INP improvement.

// ❌ ページ全体をクライアント化:不要な JS が大量にハイドレーションされる
"use client";
export default function Page() { /* 重い */ }

// ✅ 静的部分は RSC のまま。対話する葉だけ client にする
export default function Page() {
  return (
    <>
      <ProductInfo />        {/* RSC:JS ゼロ */}
      <AddToCartButton />    {/* "use client" の小さな葉だけ */}
    </>
  );
}

2-2. Split long tasks and yield to the main thread

Mark heavy updates as "non-urgent" with useTransition / useDeferredValue to protect input responsiveness.

"use client";
import { useState, useDeferredValue } from "react";

function Search({ items }: { items: Item[] }) {
  const [query, setQuery] = useState("");
  // 入力は即時反映、重いフィルタリングは「遅延」させて応答性を確保
  const deferred = useDeferredValue(query);
  const results = useMemo(() => filterItems(items, deferred), [items, deferred]);

  return (
    <>
      <input value={query} onChange={(e) => setQuery(e.target.value)} aria-label="検索" />
      <ResultList results={results} />
    </>
  );
}

Further effective moves:

  • Keep event handlers light. Don't do heavy computation in a handler; if needed, split into chunks with scheduler.yield() (supported environments) or setTimeout.
  • Debounce / throttle. Rate-limit continuous input, scroll, and resize.
  • Defer third-party JS. Push analytics, chat, and ads back with next/script's strategy="lazyOnload", etc.
  • Keep the DOM small. Virtualize (windowing) huge lists to suppress rendering cost.

Note: in apps with real-time editing or frequent re-rendering (collaborative editing, score entry, etc.), INP directly becomes "snappiness." The granularity design of state updates sways the UX.


3. Get LCP under 2.5 seconds

LCP is the time until "the largest element (often a hero image or heading)" becomes visible. In order of effectiveness.

3-1. Images: next/image and priority

import Image from "next/image";

// LCP になるヒーロー画像には priority を付け、プリロードさせる
<Image
  src="/hero.webp"
  alt="サービスのヒーロー画像"
  width={1200}
  height={630}
  priority            // ← LCP 画像に必須遅延読み込みを無効化しプリロード
  sizes="100vw"
/>

next/image automatically does WebP/AVIF conversion, responsive generation, and lazy loading. But attach priority only to the LCP image (a frequent forgotten-to-attach bug). Conversely, don't attach priority to off-screen images.

3-2. Fonts: erase FOUT/FOIT with next/font

import { Noto_Sans_JP } from "next/font/google";

const noto = Noto_Sans_JP({
  subsets: ["latin"],
  display: "swap",      // テキストを即表示(不可視待ちを避ける)
  preload: true,
});

next/font self-hosts the font at build time and erases external requests. With display: "swap", it avoids "the time text is invisible (FOIT)" while suppressing shifts from glyph differences (CLS) with size-adjust-equivalent adjustments.

3-3. Server: TTFB and SSR/streaming

  • Static generation, caching. As much as possible, front-load server processing with static generation (SSG) / ISR / PPR.
  • Streaming and <Suspense>. Without waiting for heavy data, first stream the skeleton (and the LCP element).
  • preconnect / dns-prefetch. Front-load connections to essential external origins (CDN, measurement).

4. Bring CLS near zero

CLS is "the amount elements jolt during loading." The cause concentrates in "elements appearing later whose dimensions aren't reserved."

  • Explicit dimensions for images, video, iframes, and ads. Always specify width/height (or aspect-ratio) to reserve the area first.
  • Countermeasure for font-swap shifts. next/font + display: swap + metric adjustment.
  • Countermeasure for scrollbar shifts. Prevent horizontal shift on appearance with scrollbar-gutter: stable (see the Tailwind v4 guide).
  • Don't push down from above with dynamic insertion. For banners/notifications, reserve the area or use an overlay that doesn't move the layout.
  • Show skeleton UI at the same dimensions as the real content.
/* 後から入る要素も含め、領域を先に確保する */
.thumb {
  aspect-ratio: 16 / 9; /* 画像読込前から高さが確定し、ズレない */
}

5. Measurement: stop guessing, improve with field numbers

5-1. Take field measurement (real users) yourself

Collect real-user values with the web-vitals library and send them to an analytics foundation. In Next.js, you can use useReportWebVitals.

"use client";
import { useReportWebVitals } from "next/web-vitals";

export function WebVitals() {
  useReportWebVitals((metric) => {
    // 例:計測ビーコンで送信(sendBeacon は離脱時も送れる)
    const body = JSON.stringify({ name: metric.name, value: metric.value, id: metric.id });
    navigator.sendBeacon?.("/api/vitals", body);
  });
  return null;
}
  • The lab (Lighthouse / PageSpeed Insights) is for reproduction and debugging. Build it into CI to detect regressions.
  • The field (CrUX / your own RUM) is decisive for pass/fail. Look at the distribution of devices, networks, and regions and improve the 75th percentile.

5-2. Operate it as observability

  • Record the 3 metrics per release so you notice degradation (resilience).
  • Decompose metrics by route and by device to grasp where the bottleneck localizes.
  • Dashboarding "speed" keeps improvement continuous. Optimization without measurement tends to be a YAGNI violation.

6. Next.js-specific effective points (cheat sheet)

ProblemMeans
Too much unnecessary JS (INP)RSC-ify, confine "use client" to leaves, lazy-load with next/dynamic
Slow LCP imagenext/image + priority + appropriate sizes
Delay/shift from fontsnext/font + display: swap
Slow server responseSSG / ISR / PPR, <Suspense> streaming, caching
Heavy third partiesAdjust next/script's strategy, load only when needed
Layout shiftDimension reservation, aspect-ratio, scrollbar-gutter: stable

It's KISS to crush, in order, the bottlenecks identified by measurement — not "optimize everything for now."


7. Antipatterns

  • Satisfied with Lighthouse 100. Pass/fail is the field (real-user 75%ile). Don't look only at the lab.
  • "use client" on the whole page. The main cause of INP worsening. Limit it to interactive leaves only.
  • Forgetting priority on the LCP image / attaching it to off-screen images.
  • Not specifying dimensions for image/ad slots. CLS jumps.
  • Loading third-party JS synchronously in <head>. Blocks the main thread.
  • Sacrificing accessibility for speed. Even with virtualization or skeletons, maintain focus and reading order.

8. FAQ

Q. What's the difference between INP and FID? A. FID measured only "the delay of the first operation," but INP measures the responsiveness of all operations during the page stay. In March 2024, INP officially replaced FID.

Q. Do Core Web Vitals affect SEO ranking? A. They're one ranking factor. But it's not "good = always top"; content quality is the premise. It's a differentiator between comparable content, and works indirectly via bounce rate and CV too.

Q. SPA or SSR, which is advantageous? A. Generally SSR / RSC is advantageous for LCP and INP. Less JS is sent to the client, and the initial render is faster.

Q. Is next/image alone enough for image optimization? A. It covers most, but the effect comes only when you include the LCP image's priority, appropriate sizes, format (WebP/AVIF), and dimension reservation.

Q. Where should I start? A. First measurement (field). Then the most failure-prone INP (JS reduction), followed by LCP (image, font, SSR), then CLS (dimension reservation) — that order is the most cost-effective.


Conclusion: speed is "measurable quality" and affects revenue

Core Web Vitals quantify user experience. Improvement directly connects to improving search inflow, bounce rate, and conversion.

  1. Start from measurement (field). Don't believe only the lab.
  2. For INP, reduce JS. RSC-ifying and localizing "use client" is the heart.
  3. For LCP, images, fonts, SSR. priority and next/font reliably.
  4. For CLS, dimension reservation. Erase shifts with aspect-ratio and scrollbar-gutter.
  5. Operate it as observability. Have a mechanism to notice degradation.

Perceived speed is trust in the product itself. A slow site isn't valued no matter how excellent its features.

If you need to improve your site's display speed and Core Web Vitals, or to newly develop a fast, responsive app, feel free to consult me. The case study below introduces the process of designing and implementing a responsiveness-focused real-time app that runs snappily even with multiple people operating simultaneously.

友田

友田 陽大

Developer of a METI Minister's Award–winning product. With TypeScript + Python + AWS, I deliver SaaS, industry DX, and production-grade generative AI (RAG) end to end — from requirements to infrastructure and operations — single-handedly.

Got a challenge?

From design to implementation and operations — solo × generative AI

Implementation like this article's, end to end from requirements to production. Start with a free 30-minute technical consult and tell me about your situation.

Available for both project-based (contract) and advisory engagements. Start with a free 30-minute consult.

Also worth reading