- Next.js
- Asset Optimization & Performance
- Measuring and Optimizing Core Web Vitals in Production
Measuring and Optimizing Core Web Vitals in Production
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
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.tsx2'use client';34import { useReportWebVitals } from 'next/navigation';56export function WebVitalsReporter() {7 useReportWebVitals((metric) => {8 // Metrics tracked: LCP, INP, CLS, FCP, TTFB9 const body = JSON.stringify({10 id: metric.id, // Unique ID for the metric occurrence11 name: metric.name, // 'LCP' | 'INP' | 'CLS' | 'FCP' | 'TTFB'12 value: metric.value, // Calculated performance value13 rating: metric.rating, // 'good' | 'needs-improvement' | 'poor'14 pathname: window.location.pathname,15 });1617 // Use navigator.sendBeacon to ensure data is transmitted18 // 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 });2627 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
Does server-side rendering improve or hurt INP?
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.
How do you debug INP interactions locally?
Use the Chrome DevTools Performance panel with "Enable interaction log" checked to identify long-task culprits.
