Core Web Vitals
debt(d3/e5/b5/t7)
Closest to 'default linter catches the common case' (d3). The term's detection_hints list Lighthouse, PageSpeed Insights, CrUX Dashboard, and Chrome DevTools — these are readily available tools that catch CWV issues automatically. However, they require intentional setup and checking rather than being built into CI by default, so d3 not d1.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix mentions preloading hero images, adding width/height to images, and reducing main-thread JavaScript — these span HTML, CSS, and JS changes across templates and components. Not architectural rework, but not a one-line fix either.
Closest to 'persistent productivity tax' (b5). Applies_to is web context, and CWV affects all public-facing pages. Every new feature, image, ad, or embed must be evaluated for LCP/CLS/INP impact. The performance tax is ongoing but doesn't define system architecture — it's a persistent consideration across many work streams.
Closest to 'serious trap' (t7). The misconception explicitly states developers believe high Lighthouse scores mean passing CWV, but Google uses CrUX field data which 'regularly diverge significantly' from lab scores. This contradicts how performance testing works elsewhere (lab tests = production reality), making the 'obvious' approach of optimising Lighthouse misleading.
Also Known As
TL;DR
Explanation
Core Web Vitals are a subset of Web Vitals chosen by Google because they best correlate with real user perception of page quality. LCP (Largest Contentful Paint) measures how fast the main content loads — target under 2.5s. INP (Interaction to Next Paint, replaced FID in March 2024) measures responsiveness to all user interactions — target under 200ms. CLS (Cumulative Layout Shift) measures visual stability — target under 0.1 (unexpected layout shifts that frustrate users). The critical distinction: Lighthouse measures lab performance (simulated conditions); Chrome User Experience Report (CrUX) measures field performance (real users). Google's ranking signal uses field data, not lab scores — a page can score 95 in Lighthouse and still fail Core Web Vitals in the field due to real user device and network conditions.
Common Misconception
Why It Matters
Common Mistakes
- Optimising only for Lighthouse scores while ignoring CrUX field data — real users on mid-range Android devices on 4G often have dramatically worse scores.
- Lazy-loading the LCP image — the largest above-fold image must load immediately; lazy loading it is the single most common LCP regression.
- Late-loading content above existing content — ads, cookie banners, and embeds injected after load cause CLS if they push down visible content.
- Heavy JavaScript event handlers — long-running JS on click or touch delays INP even if the page loads quickly.
- No explicit image width and height — browser cannot reserve space, images load and shift content (CLS).
Avoid When
- Do not use Lighthouse as the sole measure of Core Web Vitals compliance — lab data does not reflect real user conditions.
- Do not lazy-load above-fold images in the name of performance — it directly harms LCP.
When To Use
- Use Core Web Vitals as the primary performance benchmark for any public-facing web page.
- Measure field data in CrUX or PageSpeed Insights as the authoritative source — not Lighthouse lab scores.
- Prioritise LCP fixes first (loading), then CLS (layout stability), then INP (interactivity).
Code Examples
<!-- LCP regression: lazy loading the hero image -->
<img src="hero.jpg" alt="Hero" loading="lazy">
<!-- CLS regression: no image dimensions -->
<img src="product.jpg" alt="Product">
<!-- CLS regression: content injected above existing content -->
<script>
// After load, inject banner at top of page
document.body.insertBefore(banner, document.body.firstChild);
</script>
<!-- LCP fix: eager + high priority -->
<img src="hero.jpg" alt="Hero" fetchpriority="high" loading="eager"
width="1200" height="600">
<!-- CLS fix: explicit dimensions + lazy for below-fold -->
<img src="product.jpg" alt="Product" loading="lazy"
width="400" height="400">
<!-- CLS fix: reserve space for injected content -->
<div style="min-height: 60px">
<!-- Banner loads here without shifting content -->
</div>