Next.js Curriculum
HardAsset Optimization100% Free35 min read

Measuring and Optimizing Core Web Vitals in Production

The 30-Second Interview Answer

Core Web Vitals represent Google's key user experience metrics: Largest Contentful Paint (LCP ≤ 2.5s), Interaction to Next Paint (INP ≤ 200ms), and Cumulative Layout Shift (CLS ≤ 0.1). In Next.js, real-user monitoring (RUM) is captured via the useReportWebVitals hook or OpenTelemetry and sent to monitoring backends (Datadog, Grafana, Vercel Speed Insights).

Learning Objectives

Capture real-user Core Web Vitals metrics using useReportWebVitals.

Diagnose and remediate poor Interaction to Next Paint (INP) caused by heavy event handlers.

Optimize Largest Contentful Paint (LCP) through streaming, preloading, and edge caching.

Send custom metric beacons to an internal analytics or Datadog endpoint.

Prerequisites & Background Knowledge
Google Core Web Vitals specifications (LCP, INP, CLS)Browser performance APIBeacon API

Why Interviewers Care

High-level frontend system design question. Companies tie Core Web Vitals directly to SEO rankings and e-commerce conversion rates.

The Challenge

Explain how to measure and monitor Core Web Vitals in a Next.js production application. Implement a custom metrics reporter using useReportWebVitals and explain the transition from FID to INP.

Code Architecture & Implementation

1// app/components/WebVitalsReporter.tsx
2'use client';
3
4import { useReportWebVitals } from 'next/navigation';
5
6export function WebVitalsReporter() {
7 useReportWebVitals((metric) => {
8 // Metrics tracked: LCP, INP, CLS, FCP, TTFB
9 const body = JSON.stringify({
10 id: metric.id, // Unique ID for the metric occurrence
11 name: metric.name, // 'LCP' | 'INP' | 'CLS' | 'FCP' | 'TTFB'
12 value: metric.value, // Calculated performance value
13 rating: metric.rating, // 'good' | 'needs-improvement' | 'poor'
14 pathname: window.location.pathname,
15 });
16
17 // Use navigator.sendBeacon to ensure data is transmitted
18 // even if the user navigates away or closes the tab!
19 const url = '/api/telemetry/vitals';
20 if (navigator.sendBeacon) {
21 navigator.sendBeacon(url, body);
22 } else {
23 fetch(url, { body, method: 'POST', keepalive: true });
24 }
25 });
26
27 return null;
28}

Architectural Explanation

useReportWebVitals hooks directly into the browser PerformanceObserver API via Google's web-vitals library. When a metric stabilizes (e.g. LCP after page load, or INP upon page unload), the callback fires. Using navigator.sendBeacon guarantees delivery without delaying navigation or keeping sockets open. Optimizing INP in Next.js requires breaking long tasks into smaller chunks with startTransition, avoiding main-thread JSON parsing, and optimizing React re-renders.

Common Mistakes & Anti-Patterns

Testing Web Vitals only on high-end developer MacBooks instead of throttled mobile devices with Real User Monitoring (RUM).

Using `fetch()` without `keepalive: true` on page unload, causing beacon drops.

Confusing Lab data (Lighthouse runs) with Field data (real user 75th percentile scores in Chrome UX Report).

Interview Follow-Up Traps

The interviewer asks: "Why did Google replace FID with INP?" FID only measured the delay between a user's first click and when the main thread started processing it. INP measures the entire time until the browser actually paints the updated frame across EVERY interaction during the entire session.

Frequently Asked Questions

What are the good threshold scores for the 3 Core Web Vitals?

LCP ≤ 2.5 seconds; INP ≤ 200 milliseconds; CLS ≤ 0.1 at the 75th percentile of real-user visits.

Heavy hydration after SSR can hurt INP if the user clicks while the main thread is busy hydrating. React 19 selective hydration and RSC minimize this.

Use the Chrome DevTools Performance panel with "Enable interaction log" checked to identify long-task culprits.