Guide
Core Web Vitals explained: LCP, INP, and CLS
Google uses Core Web Vitals to quantify how real visitors experience your pages: how fast the main content appears, how quickly the page responds to taps and clicks, and whether layout jumps around while loading. These three metrics feed Search Console reports, Lighthouse audits, and — indirectly — how much organic traffic and ad revenue a content site can earn. This guide explains what each metric captures, the thresholds Google considers "good," and fixes that work on static sites and heavy web apps alike.
Why Core Web Vitals matter
Lab tools like Lighthouse run in a controlled browser and give you a snapshot. Core Web Vitals are different: they come from the Chrome User Experience Report (CrUX), aggregated from real Chrome sessions (with privacy safeguards). Google evaluates your site at the 75th percentile — if at least 75% of page loads meet the "good" threshold for each metric, that URL passes.
Passing is not a magic ranking button, but failing badly correlates with higher bounce rates, lower ad viewability, and weaker SEO on competitive queries. For publishers monetizing with display ads, slow LCP means fewer impressions load before the user leaves; high CLS pushes ad slots (and CTAs) under the user's thumb at the wrong moment. Performance is part of the product — the same philosophy behind building for end-user utility first, not developer convenience.
The three metrics at a glance
| Metric | Measures | Good | Needs work | Poor |
|---|---|---|---|---|
| LCP | Largest Contentful Paint — when the biggest visible element renders | ≤ 2.5 s | 2.5–4.0 s | > 4.0 s |
| INP | Interaction to Next Paint — responsiveness to input (replaced FID in 2024) | ≤ 200 ms | 200–500 ms | > 500 ms |
| CLS | Cumulative Layout Shift — unexpected visual movement | ≤ 0.1 | 0.1–0.25 | > 0.25 |
INP replaced First Input Delay (FID) as a Core Web Vital. FID only measured the delay before the browser could start handling the first interaction; INP tracks latency across all interactions until leave, which better reflects sluggish SPAs and long main-thread tasks.
Largest Contentful Paint (LCP)
LCP marks when the largest above-the-fold element finishes rendering — usually a hero image, a large text block, or a video poster. It stops updating after user input or when the page goes to background, so it reflects perceived load speed, not total page weight.
Common LCP culprits
- Slow server response (TTFB) — cold starts, uncached HTML, or distant origin. Fix with CDN caching, edge HTML, or static generation.
- Render-blocking CSS and JS — large stylesheets or synchronous scripts in
<head>delay first paint. Inline critical CSS; defer non-essential scripts. - Unoptimized hero images — a 2 MB PNG as your LCP element kills mobile scores. Serve WebP/AVIF, responsive
srcset, and explicitwidth/height. - Client-side rendering delay — blank shell until JavaScript hydrates. Prefer server-rendered or static HTML for content pages; hydrate progressively.
- Web fonts blocking text — invisible text until fonts load. Use
font-display: swapand preload only the weights you need.
Quick wins
Preconnect to your font and CDN origins (rel="preconnect"), preload the
LCP image if it is predictable, and compress HTML at the edge. On content sites,
the LCP element is often the headline plus lead paragraph — keep that markup in the
initial HTML response, not behind a React effect.
Interaction to Next Paint (INP)
INP measures the latency from a user interaction (click, tap, key press) until the browser paints the next frame showing visual feedback. High INP feels like "the site is frozen" even when LCP looked fine.
What drives bad INP
- Long JavaScript tasks — anything blocking the main thread for 50 ms+ queues input. Split work with
requestIdleCallback, web workers, orscheduler.yield()where supported. - Heavy event handlers — synchronous layout reads after writes (layout thrashing), large DOM diffs on every keystroke.
- Third-party scripts — tag managers, chat widgets, and ad loaders competing for the main thread. Load ads after first interaction or use lazy-on-view placement.
- Large hydration — attaching listeners to thousands of nodes on load. Event delegation and smaller interactive islands help.
Wallet connect flows and in-browser games are especially sensitive: signing popups plus animation loops plus analytics can stack. If you ship interactive checkout like our accept Solana payments guide, keep the pay button handler thin — defer verification polling off the critical path.
Cumulative Layout Shift (CLS)
CLS sums unexpected layout movement during the page lifetime. User-initiated shifts (opening a menu you clicked) do not count; an ad slot expanding and pushing the "Buy" button down does.
Typical CLS sources
- Images and embeds without dimensions — always set
widthandheight(or aspect-ratio CSS) so the browser reserves space. - Web fonts swapping metrics — fallback and webfont with different line heights reflow paragraphs. Match metrics with
size-adjustor a close fallback stack. - Injected banners and consent modals — reserve space or slide in without pushing content.
- Dynamic ad insertion — the classic publisher CLS problem. Fixed-size containers, lazy load below the fold, and avoid inserting slots above existing content after paint.
Display-ad monetization and CLS pull in opposite directions unless you design for it from the start: stable article column width, ad slots with min-height, and no late-loading widgets above the fold. That tradeoff is worth planning before you paste ad tags into every template.
How to measure Core Web Vitals
Field data (what Google uses)
- Google Search Console — Core Web Vitals report grouped by URL groups; shows mobile vs desktop CrUX buckets.
- PageSpeed Insights — CrUX field data plus lab Lighthouse for a single URL.
- Chrome DevTools → Performance insights — local profiling with INP breakdown when throttling CPU.
web-vitalslibrary — send RUM beacons to your analytics for per-route dashboards.
Lab data (for debugging)
Lighthouse in Chrome DevTools or CI gives reproducible scores but often pessimistic INP on dev machines. Use lab runs to test fixes; use field data to confirm real users improved. Test mobile with 4x CPU slowdown — desktop fiber hides main-thread sins.
A practical optimization workflow
- Pick one template — article, homepage, or checkout. CrUX groups similar URLs; fix the template, not every URL individually.
- Identify the LCP element — DevTools Performance panel labels it. Optimize that one resource first.
- Audit third parties — disable tags one at a time; measure INP delta.
- Reserve space for dynamic content — ads, embeds, fonts. CLS fixes are often one-line dimension attributes.
- Ship and wait 28 days — CrUX rolls on a 28-day window. Deploy, then check Search Console next month.
Static content sites have an advantage: HTML arrives ready to read. Our Garden Dice build log documents choices like pre-rendered OG images and schema markup — the same mindset applies here: ship HTML that works before JavaScript, keep bundles small, and measure after deploy.
Checklist for content publishers
- Hero image under 150 KB on mobile; modern formats with fallbacks.
- Critical article text in first HTML chunk — no spinner-only shells for SEO pages.
- Ad slots with min-height; load below-the-fold ads after LCP when possible.
- Defer non-critical JS; one analytics provider beats five.
- Preconnect CDN and font origins; subset fonts to used glyphs.
- Monitor Search Console CWV report monthly; fix "Poor" URL groups first.