Core Web Vitals Data Study 2026: Lab Tests of 219 Websites

We lab-tested 219 random homepages on throttled mobile: 25.4% had a good LCP, 80.6% a good CLS, median TBT 1.6 s. Results by framework, method and script.

· BugViso Team

12 min read

Key findings: On a throttled mobile profile, only 25.4% of 201 randomly sampled homepages had a good Largest Contentful Paint (≤ 2.5 s), while 80.6% had a good Cumulative Layout Shift (≤ 0.1). The median page blocked the main thread for 1.62 s (Total Blocking Time), and just 23.4% passed both LCP and CLS. Loading speed and main-thread work, not layout stability, are where most sites fail.

These are lab measurements: one controlled load per site under Lighthouse-style mobile throttling. They show how pages behave on a slow device and network, which is what most diagnostic tools (including PageSpeed Insights' lab section) report. For comparison, real-user field data from the Chrome UX Report is included below.


Method

Sample219 valid homepages drawn at random from Tranco ranks 1,001–50,000 (list 94GG2, seed 20261003); 201 completed, 18 timed out or failed
Date8 October 2026
Device profile412×823 mobile viewport, mobile user-agent, 4× CPU slowdown
Network profile150 ms round-trip time, 1.6 Mbps down, 750 Kbps up
MetricsLCP, CLS, FCP and TTFB from Google's web-vitals library; Total Blocking Time from Long Tasks after first paint
LoadWait for load, then 5 more seconds; no user interaction (so no INP)

Third-party sites in the sample aren't named.


Results: Lab Core Web Vitals on Mobile

MetricMedian75th percentileGoodNeeds improvementPoor
LCP (good ≤ 2.5 s, poor > 4 s)4.60 s9.38 s25.4%17.4%57.2%
CLS (good ≤ 0.1, poor > 0.25)0.0050.07580.6%8.5%10.9%
TBT (good ≤ 200 ms, poor > 600 ms)1,620 ms3,240 ms14.9%10.4%74.6%
FCP (good ≤ 1.8 s, poor > 3 s)2.45 s4.68 s30.3%30.3%39.3%
TTFB (good ≤ 800 ms, poor > 1.8 s)795 ms1,400 ms50.5%33.0%16.5%
CombinedSitesShare
Good LCP and good CLS4723.4%
Good LCP, CLS and TBT (TBT as a lab stand-in for responsiveness)147.0%

The median homepage transferred 768 KB in 63 requests.

How lab compares with field data

Real-user data is kinder because most visitors have faster devices and networks than a 4×-throttled lab run. The HTTP Archive's 2024 Web Almanac performance chapter, based on Chrome UX Report field data, found:

Field data (CrUX, 2024)MobileDesktop
Good LCP59%74%
Good CLS79%72%
Good INP74%97%
Pass all Core Web Vitals43%54%

CLS lines up closely between our lab sample (80.6%) and the field (79% mobile). LCP doesn't: 25.4% in our throttled lab run versus 59% of mobile origins in the field. That gap is the cost of a slow device and network, and the reason lab tests are good at finding problems that a share of your real users do hit.


Results by Front-End Framework

We matched each site with the framework detected in our raw-vs-rendered study of the same sample (groups with at least five sites):

FrameworkSitesMedian LCPMedian TBTGood LCP
No framework detected1134.43 s1,102 ms26%
React (any)405.56 s2,379 ms22%
WordPress273.99 s2,711 ms37%
Next.js194.77 s2,937 ms32%
Vue135.79 s2,585 ms0%
Astro62.03 s822 ms67%

Framework labels overlap (a Next.js site is also a React site), and some groups are small, so treat this as direction, not ranking. The pattern is still clear: pages that ship little JavaScript (Astro's default) loaded fastest, and JavaScript-heavy stacks carried the most main-thread blocking. See our Astro SEO guide and unused JavaScript statistics, where the median site shipped 55.9% unused JavaScript.


What the Data Says to Fix First

  1. LCP is the most common failure. 57.2% were poor in the lab. Start with server response (half of sites had a TTFB over 800 ms on this profile) and the size and priority of the LCP resource. Our guide to improving LCP under 2.5 s walks through it.
  2. Main-thread work is the second. Three quarters of pages had poor TBT, which predicts slow responses to the first taps. See INP explained.
  3. CLS is mostly solved, but one in ten pages still shifted badly; reserve space for images, embeds and banners.

Measure Your Own Pages the Same Way

This script runs the study's lab profile against one URL:

python
"""Lab Core Web Vitals for one URL on a throttled mobile profile.

Same profile as the BugViso 219-site study: 412x823 mobile viewport, 4x CPU,
150 ms RTT, 1.6 Mbps down. Reports LCP, CLS, TBT, FCP and TTFB.
Usage:
  pip install playwright && python -m playwright install chromium
  python cwv_lab.py https://example.com/
"""
import sys
from playwright.sync_api import sync_playwright

INIT = """window.__v = {cls: 0, lt: []};
new PerformanceObserver(l => { for (const e of l.getEntries()) if (e.name === 'first-contentful-paint') __v.fcp = e.startTime; }).observe({type: 'paint', buffered: true});
new PerformanceObserver(l => { for (const e of l.getEntries()) __v.lcp = e.startTime; }).observe({type: 'largest-contentful-paint', buffered: true});
new PerformanceObserver(l => { for (const e of l.getEntries()) if (!e.hadRecentInput) __v.cls += e.value; }).observe({type: 'layout-shift', buffered: true});
new PerformanceObserver(l => { for (const e of l.getEntries()) __v.lt.push([e.startTime, e.duration]); }).observe({type: 'longtask', buffered: true});"""

COLLECT = """() => {
  let tbt = 0;
  for (const [s, d] of __v.lt) if (s + d > (__v.fcp || 0) && d > 50) tbt += d - 50;
  const nav = performance.getEntriesByType('navigation')[0];
  return {LCP: __v.lcp, CLS: __v.cls, TBT: tbt, FCP: __v.fcp, TTFB: nav ? nav.responseStart : null};
}"""
GOOD = {"LCP": 2500, "CLS": 0.1, "TBT": 200, "FCP": 1800, "TTFB": 800}


def main(url):
    with sync_playwright() as p:
        browser = p.chromium.launch()
        ctx = browser.new_context(viewport={"width": 412, "height": 823}, is_mobile=True, has_touch=True)
        ctx.add_init_script(INIT)
        page = ctx.new_page()
        cdp = ctx.new_cdp_session(page)
        cdp.send("Network.enable")
        cdp.send("Network.emulateNetworkConditions", {"offline": False, "latency": 150,
                 "downloadThroughput": 1.6 * 1024 * 1024 / 8, "uploadThroughput": 750 * 1024 / 8})
        cdp.send("Emulation.setCPUThrottlingRate", {"rate": 4})
        page.goto(url, wait_until="load", timeout=60000)
        page.wait_for_timeout(5000)
        m = page.evaluate(COLLECT)
        browser.close()
    for k, v in m.items():
        if v is None:
            print(f"{k:5} n/a")
            continue
        unit = "" if k == "CLS" else " ms"
        shown = f"{v:.3f}" if k == "CLS" else f"{v:.0f}"
        print(f"{k:5} {shown + unit:>10}  {'good' if v <= GOOD[k] else 'needs work'} (good <= {GOOD[k]}{unit})")


if __name__ == "__main__":
    main(sys.argv[1])
text
LCP      2550 ms  needs work (good <= 2500 ms)
CLS        0.000  good (good <= 0.1)
TBT       304 ms  needs work (good <= 200 ms)
FCP      2550 ms  needs work (good <= 1800 ms)
TTFB      842 ms  needs work (good <= 800 ms)

Run it three to five times and take the median; single lab runs vary.


How BugViso Measures Core Web Vitals

A BugViso audit captures LCP, CLS, INP (when an interaction occurs), FCP and TTFB with the same self-hosted web-vitals library, reloads the page under emulated Slow 3G and Fast 3G with CPU throttling, measures unused JavaScript and CSS through Chrome DevTools coverage, and attributes the worst long task to its script. When real-user CrUX data or a PageSpeed lab run is available, the score uses it and labels the source, so a lab number is never presented as field data.

You can see every rule BugViso applies in its Core Web Vitals checker.


Limitations

  • Lab, not field. One throttled load per site; real users' devices and networks vary.
  • Homepages only. Inner pages often perform differently.
  • One location and machine. Network distance and the test machine's CPU affect absolute numbers; compare relative results.
  • No INP. INP needs real interactions; TBT is reported as a stand-in for main-thread risk.

FAQ

What percentage of websites pass Core Web Vitals?

In real-user data from the 2024 Web Almanac, 43% of websites passed all three on mobile and 54% on desktop. In our throttled lab test, 23.4% of homepages had both a good LCP and a good CLS.

Which Core Web Vital fails most often?

LCP. In our lab sample, 57.2% of homepages had a poor LCP, compared with 10.9% for CLS.

Why do lab and field Core Web Vitals differ?

Lab tests use one fixed, deliberately slow profile. Field data aggregates real visitors, most of whom have faster devices and connections, at the 75th percentile.

Is lab data useful if Google uses field data?

Yes. Lab data reproduces problems on demand, so you can find and fix them; field data tells you how many real users are affected.


The Takeaway

Most sites pass on layout stability and fail on loading speed and main-thread work, which a BugViso performance audit breaks down page by page.

Found this useful? Share it.

See where your site stands

Run a free BugViso audit for SEO, speed, accessibility and AI search readiness — with fixes you can ship today.