#Next.js#React

Web Vitals: Lab Data vs. Field Data

A high Lighthouse score does not guarantee a fast user experience. This article explains the gap between simulated lab data and real-user field data, why field results are authoritative, how to collect them with CrUX, the web-vitals library, and RUM, and when to use each data source.

Key Points

  • Topic Focus: Next.js、React
  • Suitable for: indie hackers, full-stack engineers, AI application developers
  • Core value: quickly understand background, practical paths, common pitfalls, and engineering advice
Author @admin·Published July 26, 2026·Updated July 26, 2026

Anyone who has worked on performance optimization has probably encountered this scenario: you run Lighthouse locally, score 98, see green across every metric, and feel pretty good about it. Then, a week after launch, users complain that "the page is laggy" or "it won't open," and your boss confronts you with screenshots. You check monitoring and discover that real users' median LCP is three times your local result.

Your measurement was not wrong. You simply were not measuring the same thing.

Web Vitals has two data sources: Lab Data and Field Data. They measure completely different things. Ignore that distinction, and your performance optimization efforts may keep going in circles.

1. Lab Data: An "Ideal Result" Produced in a Clean Environment

As the name suggests, lab data is collected in a controlled, simulated environment. The primary tools are Lighthouse (the one built into Chrome DevTools, used by PageSpeed Insights, and run in CI) and WebPageTest.

It works roughly like this:

  • A machine with known specifications (usually a fixed-spec server in a data center or a CI runner);
  • A simulated network throttling profile (for example, 4G = 9Mbps bandwidth / 170ms RTT);
  • Simulated CPU slowdown (4x throttling);
  • A brand-new browser session with an empty cache;
  • The page is loaded and a set of predefined interactions is performed (or no interaction occurs and only loading is measured);
  • The tool produces a complete set of metrics and scores.

Its core advantage is reproducibility: with the same commit and configuration, today's and tomorrow's results should be nearly identical. That means you can use it for regression comparisons: "Did this change reduce LCP from 2.1s to 1.8s?" The answer is unambiguous.

But its fatal weakness is precisely that "controlled environment." A simulated environment is never the real environment.

2. Field Data: The "Truth" Collected from Real Users

Field data, also called RUM (Real User Monitoring), is reported by the browsers of real users visiting your website.

During every real visit, the user's browser measures metrics such as LCP, INP, and CLS, then uses a small piece of JS (Beacon / fetch) to send those values, along with dimensions such as device type, network conditions, and page path, to your server or to Google.

Field data reflects the real distribution. Some users have an iPhone 15 Pro and a gigabit home fiber connection. Others have a three-year-old budget Android phone and an unstable 4G signal. Still others have 50 tabs open, eight ad-blocking extensions installed, and a system update downloading in the background. Every user's experience is one data point, and only the aggregate reveals how your website actually performs "in the wild."

Lab data tells you "how fast this page can be"; field data tells you "how fast this page actually is."

3. Why the Gap Can Be Enormous

Many teams are shocked the first time they seriously compare lab and field data. The gap is often 2-5x. The reasons are straightforward:

1. Users' devices are much slower than your Mac. You run Lighthouse on an M2 MacBook Pro, and even after 4x CPU throttling, it may still be faster than the CPU in a midrange Android phone. On a real user's low-end device, running React hydration may take 1.5s, while it takes only 200ms locally.

2. Users' networks are much slower than your fiber connection. Simulated 4G is "ideal 4G": a stable 9Mbps and 170ms RTT. Real-world 4G can suddenly drop to 1Mbps, spike to 400ms RTT, and disconnect and reconnect in the subway. You may receive the first byte in 50ms, while a user waits 800ms.

3. You cannot simulate the state of a user's browser. Cache hits, Service Worker interception, resource contention from background tabs, scripts injected by extensions, actual screen dimensions and DPR, CPU throttling in battery-saver mode: Lighthouse cannot simulate any of them.

4. You cannot predict user behavior. Lighthouse effectively measures "load and stop," but real users scroll immediately, click buttons right away, and interact aggressively before the page is ready. All of this affects INP (interaction latency), which your lab test may not measure at all.

This leads to a hard rule: if an optimization improves only lab data while field data does not move, you may be optimizing a "simulated problem" rather than a real user problem.

4. Google Rankings Use Field Data, Not Lighthouse Scores

Many people get this wrong. On PageSpeed Insights, you will see two tabs: Lab Data and Field Data. The field data comes from CrUX (Chrome User Experience Report), Google's domain-aggregated performance data collected from real Chrome users.

The "Page Experience signal" used by Google Search rankings is based on CrUX field data, not Lighthouse scores. You can score 100 in Lighthouse, but if your mobile p75 LCP is 4 seconds in CrUX, your ranking can still be penalized.

CrUX has a coverage threshold: a site must have enough real traffic before Google collects and publishes its data. If a small or medium-sized site does not have sufficient traffic, PageSpeed Insights will show "Field data unavailable." In that case, you must build your own RUM, or you will have no idea what real users experience.

5. How to Collect Field Data

There are three approaches, from lightweight to comprehensive:

1. The web-vitals library (strongly recommended as the foundation of your own RUM)

Google's officially maintained web-vitals library lets you obtain LCP/INP/CLS/TTFB/FCP with just a few lines of browser-side code, then report the data wherever you want.

ts
// src/app/web-vitals.ts
import { onCLS, onINP, onLCP, onTTFB, onFCP } from 'web-vitals';

function sendToAnalytics(metric: { name: string; value: number; id: string; rating: string }) {
  // Use sendBeacon so the data can still be sent when the page unloads
  const body = JSON.stringify({
    name: metric.name,        // 'LCP' | 'INP' | 'CLS' | ...
    value: metric.value,      // Numeric value, such as LCP in milliseconds
    rating: metric.rating,    // 'good' | 'needs-improvement' | 'poor'
    id: metric.id,            // Unique ID for this session, useful for deduplication
    page: location.pathname,
    // Dimensions: add device / network / source as needed
    device: /Mobi|Android/i.test(navigator.userAgent) ? 'mobile' : 'desktop',
    referrer: document.referrer,
  });

  if (navigator.sendBeacon) {
    navigator.sendBeacon('/api/vitals', body);
  } else {
    fetch('/api/vitals', { body, method: 'POST', keepalive: true });
  }
}

export function reportWebVitals() {
  onLCP(sendToAnalytics);
  onINP(sendToAnalytics);
  onCLS(sendToAnalytics);
  onTTFB(sendToAnalytics);
  onFCP(sendToAnalytics);
}
ts
// src/app/layout.tsx(Client Component)
'use client';
import { useEffect } from 'react';
import { reportWebVitals } from '@/app/web-vitals';

export function WebVitalsBootstrap() {
  useEffect(() => { reportWebVitals(); }, []);
  return null;
}

Note the use of sendBeacon / fetch(..., keepalive: true): the final INP and CLS values are often not known until the user leaves the page. You must use an API that can send data during unload, or those measurements will be lost.

2. Send It to an Existing Backend

After the /api/vitals reporting endpoint receives the data, you can:

  • Forward it to Google Analytics 4 (using gtag('event', 'web_vitals', {...})): free and suitable for personal sites;
  • Send it to Sentry / Datadog / New Relic: these APM platforms all provide RUM modules with built-in dashboards and alerts;
  • Store it in your own database table (for example, Postgres plus a simple aggregation query) for complete control.

3. CrUX (Passive Collection)

If your site has enough traffic, Google collects the data automatically, and you can view the results directly in PageSpeed Insights or the "Core Web Vitals" report in Search Console. There is no operational overhead, but you can only see aggregated domain-level data and cannot drill down into individual pages or user groups (unless you use the CrUX API/CrUX Dashboard, which still provides monthly aggregates).

Practical recommendation: install the web-vitals library for your own RUM and use CrUX for cross-validation. The former is real-time and supports drill-down analysis; the latter is authoritative and supports competitor comparisons.

6. Analyzing Field Data: Look at p75, Not the Average

Once you have a collection of field data, do not look at the average. Performance data has an extremely long-tailed distribution (99 users at 1 second plus one user at 30 seconds gives an average of 1.29 seconds, but the user who waited 30 seconds has already left). Averages conceal the real problem.

The industry standard is p75 (the 75th percentile): sort all samples from smallest to largest and take the value at the 75% position. It means that "75% of users have an experience at least this good." Google CrUX and every Web Vitals pass/fail assessment use p75.

You should also always segment the data:

  • By device: "What is mobile p75 LCP? What about desktop?" The answers often differ by 2-3x.
  • By network: "What is p75 INP for 4G users? What about slow 3G?"
  • By page: "Which has worse CLS, the product detail page or the homepage?"
  • By route/entry source: "Do users arriving from search and social media have the same experience?"

A practical rule of thumb: "overall p75 passes" is meaningless; "mobile p75 LCP passes" is what actually matters. The worst experience always belongs to users on mobile devices and slow networks. Isolate that group to find the real problem.

7. The Right Use for Each Type of Data

Do not treat them as opposing approaches. They are complementary:

ScenarioUseWhy
Local development and immediate validation after a code changeLab data (Lighthouse)Reproducible, fast feedback, and no need to wait for user visits
CI performance regression gatesLab dataRun it on every PR; stable numbers allow you to define thresholds
Assessing whether production actually meets its targetsField data (RUM / CrUX)Reflects real experience, and Google rankings use it too
Finding experience bottlenecks for specific pages or user groupsField dataOnly field data can drill down by device and network
Comparing competitors and industry rankingsCrUXA highly comparable public dataset

In short: lab data governs the "development loop"; field data governs "business decisions."

8. Do Not Optimize Only for Lighthouse Scores

This is a real trap. I have seen teams make a pile of "simulation-specific" optimizations to push Lighthouse from 90 to 100: reducing fonts, lazy-loading everything, preconnecting to a long list of domains, and so on. Lighthouse did reach 100, but the field data barely moved. Those optimizations targeted bottlenecks amplified by simulated throttling, while real users were blocked elsewhere, perhaps by a third-party SDK, a long main-thread task, or hydration on a low-end device.

Worse, some "score-chasing" tactics actually harm the real experience. For example, aggressively delaying the loading of above-the-fold images may save resources on a fast network and capable device, but it can make LCP occur even later on weaker devices.

So here is the final hard rule:

Lighthouse is the compass during development; CrUX/RUM is the judge in production. Optimize for field data and use lab data as supporting evidence.

The next time you want to squeeze another two points out of Lighthouse, open your RUM dashboard first and ask: "Will this change reduce mobile p75 LCP?" If the answer is "I don't know," hold off on that optimization.

Summary

  • Lab data (Lighthouse) is measured in a simulated, clean, reproducible environment and is suitable for development debugging and CI regressions. Field data (RUM/CrUX) comes from real users, reflects actual experience, and is the final measure of production quality.
  • The gap comes from real-world variables: slow devices, poor networks, extensions, caches, and user behavior. Labs cannot simulate them all, so field results are usually 2-5x worse than lab results.
  • Google rankings use CrUX field data, not Lighthouse scores. Sites without enough traffic to qualify for CrUX must build their own RUM.
  • Collect field data with the web-vitals library and report it using sendBeacon. Analyze it with p75 and segmentation by device/network/page. "Mobile p75" is the meaningful standard.
  • Do not optimize only for Lighthouse scores. You may improve the simulated environment without changing the field experience at all. Treat field data as authoritative and lab data as a development aid.

Key takeaway: Lab data tells you "how fast your page can run"; field data tells you "how slow your users actually experience it." In performance optimization, the latter is the battle you ultimately need to win.

Related

Related Content