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.
Kazi Khalid Hasan Tamim · 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
| Sample | 219 valid homepages drawn at random from Tranco ranks 1,001–50,000 (list 94GG2, seed 20261003); 201 completed, 18 timed out or failed |
| Date | 8 October 2026 |
| Device profile | 412×823 mobile viewport, mobile user-agent, 4× CPU slowdown |
| Network profile | 150 ms round-trip time, 1.6 Mbps down, 750 Kbps up |
| Metrics | LCP, CLS, FCP and TTFB from Google's web-vitals library; Total Blocking Time from Long Tasks after first paint |
| Load | Wait 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
| Metric | Median | 75th percentile | Good | Needs improvement | Poor |
|---|---|---|---|---|---|
| LCP (good ≤ 2.5 s, poor > 4 s) | 4.60 s | 9.38 s | 25.4% | 17.4% | 57.2% |
| CLS (good ≤ 0.1, poor > 0.25) | 0.005 | 0.075 | 80.6% | 8.5% | 10.9% |
| TBT (good ≤ 200 ms, poor > 600 ms) | 1,620 ms | 3,240 ms | 14.9% | 10.4% | 74.6% |
| FCP (good ≤ 1.8 s, poor > 3 s) | 2.45 s | 4.68 s | 30.3% | 30.3% | 39.3% |
| TTFB (good ≤ 800 ms, poor > 1.8 s) | 795 ms | 1,400 ms | 50.5% | 33.0% | 16.5% |
| Combined | Sites | Share |
|---|---|---|
| Good LCP and good CLS | 47 | 23.4% |
| Good LCP, CLS and TBT (TBT as a lab stand-in for responsiveness) | 14 | 7.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) | Mobile | Desktop |
|---|---|---|
| Good LCP | 59% | 74% |
| Good CLS | 79% | 72% |
| Good INP | 74% | 97% |
| Pass all Core Web Vitals | 43% | 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):
| Framework | Sites | Median LCP | Median TBT | Good LCP |
|---|---|---|---|---|
| No framework detected | 113 | 4.43 s | 1,102 ms | 26% |
| React (any) | 40 | 5.56 s | 2,379 ms | 22% |
| WordPress | 27 | 3.99 s | 2,711 ms | 37% |
| Next.js | 19 | 4.77 s | 2,937 ms | 32% |
| Vue | 13 | 5.79 s | 2,585 ms | 0% |
| Astro | 6 | 2.03 s | 822 ms | 67% |
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
- 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.
- Main-thread work is the second. Three quarters of pages had poor TBT, which predicts slow responses to the first taps. See INP explained.
- 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:
"""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])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.
See where your site stands
Run a free BugViso audit for SEO, speed, accessibility and AI search readiness — with fixes you can ship today.