Core Web Vitals
Core Web Vitals
Google's three named metrics for real-world user experience — LCP, INP, and CLS — used as an SEO ranking signal.
# measured on real user visits (field data), not just lab tests
💡 Passing all three at the "good" threshold for 75% of visits is what Google calls "passing Core Web Vitals."
LCP
Largest Contentful Paint — the time it takes for the largest visible element (usually a hero image or heading) to render.
# good: < 2.5s | needs improvement: 2.5-4s | poor: > 4s
💡 Almost always dominated by one of: slow server response, render-blocking resources, or a slow-loading image.
INP
Interaction to Next Paint — measures how responsive a page feels across every click, tap, or keypress during the visit.
# good: < 200ms | needs improvement: 200-500ms | poor: > 500ms
💡 Replaced the older "FID" (First Input Delay) metric in March 2024 — FID only measured the *first* interaction; INP measures all of them.
CLS
Cumulative Layout Shift — measures how much visible content unexpectedly jumps around as the page loads.
# an ad or image loading in without a reserved size pushes the text you were about to tap down the page
💡 Fixed almost entirely by always setting explicit width/height (or aspect-ratio) on images and embeds before they load.
field data vs. lab data
Field data comes from real users on real devices and networks (CrUX); lab data comes from a controlled, simulated test run (Lighthouse).
# Lighthouse score can look great in the lab and still fail in the field on a slow 4G phone
💡 Google's actual ranking signal uses field data — lab tools are diagnostic aids, not the score that counts.
Loading & delivery
TTFB
Time to First Byte — how long the browser waits after requesting a page before the server starts sending any response back.
# high TTFB usually points to slow server-side processing, not the network itself
💡 Everything else on the page is delayed by however long TTFB takes — it's the floor under every other metric.
render-blocking resource
A CSS or JS file the browser must fully download and process before it can paint anything on screen.
<!-- blocks rendering until fully downloaded -->
<link rel="stylesheet" href="styles.css">
<script src="app.js"></script>
💡 Fixed with async/defer for scripts, and inlining or deferring non-critical CSS.
lazy loading
Deferring the loading of off-screen content (usually images) until the user actually scrolls near it.
<img src="photo.jpg" loading="lazy" alt="...">
💡 Never lazy-load the LCP image itself — that delays the exact metric you're trying to improve.
critical CSS
The minimal CSS needed to render the visible part of the page, inlined directly in the HTML so it doesn't need a separate network request.
<style>/* just the above-the-fold rules */</style>
💡 The rest of the CSS still loads normally, just not blocking the first paint.
preload / prefetch
Hints that tell the browser to fetch a resource early — preload for something needed now, prefetch for something likely needed soon.
<link rel="preload" href="hero.webp" as="image">
<link rel="prefetch" href="/next-page">
💡 Overusing preload competes with other critical resources for bandwidth — reserve it for the true LCP resource.
Rendering & JS
hydration
The process of attaching JavaScript event handlers and interactivity to server-rendered, already-visible HTML.
# page is visible instantly (SSR HTML) but not clickable until hydration finishes
💡 The gap between "visible" and "interactive" during hydration is a common source of poor INP on JS-heavy sites.
code splitting
Breaking a JavaScript bundle into smaller chunks that load on demand, instead of shipping the whole app upfront.
const Modal = lazy(() => import('./Modal'));
💡 Cuts the amount of JS the browser has to parse and execute before the page feels responsive.
main thread blocking
When JavaScript execution occupies the browser's single UI thread long enough that the page can't respond to clicks or scrolls.
# a task running > 50ms is a "long task" that can delay input response
💡 The direct cause of poor INP scores — break up long synchronous work or move it off the main thread.
above-the-fold
The portion of a page visible without scrolling — the content that matters most for perceived load speed.
# optimize the hero section first; everything below the fold can load a beat later without the user noticing
💡 Not a fixed size — it varies by device/viewport, so "the fold" is really a range, not a line.
Network & assets
HTTP/2 multiplexing
Sending many requests over a single connection simultaneously, instead of one at a time — HTTP/1.1's big limitation removed.
# HTTP/1.1: 6 parallel connections max per domain
# HTTP/2: many requests share one connection, no such cap
💡 Made old HTTP/1.1 tricks like domain sharding and CSS/JS bundling-for-fewer-requests obsolete or even counterproductive.
compression (gzip/Brotli)
Shrinking text-based assets (HTML, CSS, JS, JSON) before sending them over the network, decompressed by the browser on arrival.
Content-Encoding: br
💡 Brotli typically compresses ~15-20% smaller than gzip for the same content, at the cost of slightly slower compression time.
CDN edge caching
Serving static assets from a server geographically close to the user, cutting the network round-trip distance.
Cache-Control: public, max-age=31536000, immutable
💡 A well-cached asset with a long max-age can be served without even touching the origin server again.
image optimization
Serving images in the smallest reasonable file size — modern formats (WebP/AVIF), correct dimensions, and appropriate compression.
<img src="photo.avif" width="800" height="600" alt="...">
💡 Unoptimized images are the single most common cause of poor LCP on real-world sites.