Core Web Vitals — Interactive Reference 2026

How fast is
your page
really?

Google's framework for measuring real-user experience. Three core signals that directly affect search ranking — with live demos you can run right here.

LCP — loading
≤ 2.5s
INP — interactivity
≤ 200ms
CLS — visual stability
≤ 0.1
TBT — lab proxy
≤ 200ms
TTFB — server
≤ 800ms
FCP — first paint
≤ 1.8s
↓ scroll to explore each metric with live demos

01 / Core Web Vital
Largest Contentful
Paint LCP
Time to render the largest visible element — hero image, h1, big text block.
Measures perceived loading speed. Google's most weighted Core Vital.
LCP
Largest Contentful Paint CORE
Marks the point when the largest image or text block in the viewport becomes visible.
Threshold measured at the 75th percentile of page loads from real field data.
Good <2.5s Needs <4s Poor >4s
Common causes of poor LCP
  • Slow server response time (high TTFB)
  • Hero image not preloaded — browser discovers it too late
  • Render-blocking CSS / JS in <head>
  • Font blocking render (FOUT / FOIT)
  • loading="lazy" on above-the-fold image
  • No CDN — server far from user
  • Image served without correct dimensions (triggers reflow)
How to fix
  • <link rel="preload" as="image" href="hero.webp">
  • fetchpriority="high" on hero img
  • Serve WebP/AVIF at correct dimensions
  • CDN + HTTP/2 edge caching
  • font-display: swap or optional
<!-- ✓ Correct LCP setup --> <link rel="preload" as="image" href="hero.webp" /> <img src="hero.webp" width="1200" height="630" fetchpriority="high" /* never lazy on LCP! */ />
Live demo — LCP simulation
[ hero image not loaded ]
Above-the-fold heading text — this would be LCP candidate if largest
LCP time

02 / Core Web Vital
Interaction to
Next Paint INP
Delay from user click/tap/keypress to next browser paint. Replaced FID in 2024.
Measures responsiveness throughout the entire page lifetime — not just on load.
INP
Interaction to Next Paint CORE
Observes the latency of all clicks, taps, and keyboard interactions during page lifetime.
Reports the worst-case interaction (above the 98th percentile).
Good <200ms Needs <500ms Poor >500ms
Common causes of poor INP
  • Long JS tasks blocking main thread (>50ms)
  • Heavy event handlers without debounce/throttle
  • Third-party scripts (analytics, chat) on main thread
  • Excessive DOM nodes — slow style recalculation
  • Heavy React / Vue hydration at initial load
  • Synchronous XHR or heavy computation in handler
How to fix
  • scheduler.yield() to give browser a breath
  • Debounce / throttle heavy event handlers
  • Move heavy work to Web Worker
  • Code-split and lazy-load non-critical components
  • Use requestIdleCallback for non-urgent work
// ✗ BAD — blocks main thread btn.addEventListener('click', () => { heavySync(200); // 200ms sync loop! updateUI(); }); // ✓ GOOD — yield to browser btn.addEventListener('click', async () => { await scheduler.yield(); heavySync(200); // runs after paint updateUI(); });
Live demo — main thread blocking
Main thread timeline (50ms = 1 block):
Long task (blocked) Paint / idle Web Worker
INP latency
UI frozen
Status: idle
The "Bad" demo runs a synchronous while(Date.now() < end) loop for 300ms — you'll see the UI freeze. The counter below updates only after the task finishes.
Live counter (stops updating when main thread blocked)
0

03 / Core Web Vital
Cumulative
Layout Shift CLS
Total amount of unexpected layout shifts throughout the page lifetime.
Each shift = impact fraction × distance fraction. Sum must stay below 0.1.
CLS
Cumulative Layout Shift CORE
Measures unexpected visual instability. Score = sum of (impact_fraction × distance_fraction) for all layout shifts.
Only shifts >500ms after last input count — rapid-fire interactions don't penalise.
Good <0.1 Needs <0.25 Poor >0.25
Common causes of poor CLS
  • Images missing width / height attributes
  • Ads, embeds, iframes loaded after initial paint
  • Font swap causing text reflow (FOUT)
  • Cookie banners / popups injected above the fold
  • Animations using top / left instead of transform
  • Dynamic content injected above the fold without reserved space
How to fix
  • Always set width + height on img/video/iframe
  • Use aspect-ratio CSS for responsive containers
  • Reserve space for ads before they load
  • Use transform for animations, never top/left
  • font-display: optional to avoid FOUT
/* ✗ BAD — no size = layout shift */ <img src="hero.jpg" /> /* ✓ GOOD — browser reserves space */ <img src="hero.jpg" width="800" height="450" style="aspect-ratio: 16/9" /> /* ✗ BAD animation */ .slide { top: 0100px } /* ✓ GOOD animation */ .slide { transform: translateY(100px) }
Live demo — layout shift visualiser
This is the content you were about to read.
It's positioned here before any dynamic content loads.
Watch what happens when an ad/banner injects above it...
CLS: 0.00
CLS score
0.00

04 / Lab Metric (INP proxy)
Total Blocking
Time TBT
Sum of all periods the main thread was blocked >50ms between FCP and TTI.
This is Lighthouse's lab-mode proxy for INP. High TBT almost always means poor real-world INP.
TBT
Total Blocking Time
For each long task (>50ms), TBT = task_duration − 50ms.
Example: a 250ms task contributes 200ms to TBT. Sum all such tasks between FCP → TTI.
Good <200ms Needs <600ms Poor >600ms
Common causes of high TBT
  • Oversized JS bundle — slow to parse & execute
  • Third-party scripts: gtag, Hotjar, chat widgets, ads
  • Unnecessary polyfills for modern browsers
  • Large CSS selectors forcing style recalc
  • No code splitting — everything in one chunk
  • Heavy computation during page startup
How to fix
  • Code-split bundles + aggressive tree-shaking
  • Lazy-load third-party scripts after interaction
  • Break long tasks with setTimeout / scheduler API
  • Audit unused JS with Chrome Coverage tab
  • Use Web Worker for heavy computation
// TBT calculation example: // Task 1: 250ms → contributes 200ms TBT // Task 2: 80ms → contributes 30ms TBT // Task 3: 45ms → contributes 0ms TBT (under 50ms) // Task 4: 120ms → contributes 70ms TBT // Total TBT = 300ms ← Poor! // ✓ FIX: break task 1 into chunks async function chunkedWork(items) { for (let i = 0; i < items.length; i++) { process(items[i]); if (i % 50 === 0) await scheduler.yield(); // breathe! } }
Live demo — long task visualiser
Timeline (each block = 50ms, red = blocking >50ms threshold):
Total TBT
Long tasks

05 / Supporting Metrics
TTFB · FCP
Server & Paint
Not direct ranking signals, but high TTFB drags LCP down every time.
Fix these and Core Vitals improve as a side effect.
TTFB
Time to First Byte
Time from navigation start until the first byte of the response arrives.
The foundation all other metrics rest on — if TTFB is slow, everything is slow.
Good <800ms Poor >1.8s
Causes
  • Slow server — no caching, cold starts
  • Heavy DB queries at render time (SSR)
  • No CDN — high latency to origin
  • Multi-step redirect chains
  • Serverless function cold start
Fix
  • CDN + edge caching (Cloudflare, Vercel Edge)
  • SSG / ISR for content that doesn't change per-request
  • Stale-While-Revalidate cache strategy
  • Eliminate redirect chains
Live demo — TTFB waterfall
Navigation waterfall:
FCP
First Contentful Paint
When the first pixel of text or image appears on screen.
Different from LCP — FCP fires earlier, on the first content element, not the largest.
Good <1.8s Poor >3s
Causes
  • Render-blocking stylesheets in <head>
  • High TTFB dragging FCP down with it
  • Fonts blocking render (font-display: block)
  • JavaScript in <head> without async/defer
Fix
  • Inline critical CSS, defer the rest
  • font-display: swap or optional
  • Always async or defer scripts in <head>
  • Reduce TTFB first — FCP will follow
FCP vs LCP — what fires when
■ FCP fires at 1.2s ■ LCP fires at 2.2s (hero image)

Summary
The full
picture
All metrics, thresholds, and their relationship to each other.
LCP — CORE
≤ 2.5s
Loading — largest visible element rendered
INP — CORE
≤ 200ms
Interactivity — click to next paint latency
CLS — CORE
≤ 0.1
Stability — no unexpected layout shifts
TBT
≤ 200ms
Lab proxy for INP — long task sum
TTFB
≤ 800ms
Server speed — first byte from origin
FCP
≤ 1.8s
First pixel of text or image on screen
The dependency chain — fix in this order
1. TTFB
server speed
2. FCP
first paint
3. LCP
largest paint
4. TBT/INP
interactivity
5. CLS
stability
A slow TTFB will make LCP bad no matter what else you do. Always fix the server layer first.
TBT is only measured in Lighthouse (lab). Real-world INP is collected by Chrome from actual users (field data).
The three Core Vitals (LCP, INP, CLS) are the only ones Google uses as search ranking signals.
Quick-win checklist
Live audit — align.vn
align.vn
CWV Audit
Analysis from HTML source of align.vn/blog/what-is-a-moodboard/
Each criterion checked against Core Web Vitals standards.
CWV score
Calculating...
Đang phân tích các tiêu chí từ HTML source...
0
PASS
0
WARN
0
FAIL
Top priority fixes for align.vn