All posts
·7 min read·performance · core web vitals · seo

Core Web Vitals: what each one actually measures and how to move it

Google's Core Web Vitals decide a chunk of your search ranking and how patient your visitors are. Here is what LCP, INP, and CLS actually measure and how to move them.

Google has been using Core Web Vitals as a ranking signal since June 2021. The metrics changed in March 2024, when INP replaced FID. Most articles about Core Web Vitals haven't been updated since the change, and they're still telling people to optimize for the wrong things.

Here's what each metric actually measures in 2026, what counts as good and bad, and the specific things that move each one.

What Core Web Vitals are, and what they aren't

Core Web Vitals is the name Google gives to three specific performance metrics: LCP, INP, and CLS. Together they're supposed to capture three different feelings a user has on a page:

  1. When does the page show up? (LCP)
  2. Does it respond when I tap on it? (INP)
  3. Does the layout stay still or jump around? (CLS)

Each one has three buckets defined by Google: good, needs improvement, and poor. When Google scores your URL, it uses the 75th percentile of real Chrome users in the last 28 days. Not your synthetic Lighthouse score. Not your laptop on fiber. The 75th percentile field data, pulled from the Chrome User Experience Report (CrUX).

This distinction matters because most performance work targets the lab score, which is wrong. Your site can score 100 in PageSpeed Insights and still fail Core Web Vitals if your real users are on three-year-old Android phones.

LCP: Largest Contentful Paint

What it measures: the time from page navigation start to when the largest visible content element (an image, a video poster, a big text block) finishes rendering.

| LCP | Bucket | |---|---| | Under 2.5s | Good | | 2.5s to 4s | Needs improvement | | Over 4s | Poor |

What's almost always the LCP element:

  • The hero image
  • A large above-the-fold heading
  • A video's poster image
  • A background image on the hero section

Common causes of bad LCP:

  1. Hero image not preloaded. The browser doesn't know it's important until the CSS finishes parsing. Add <link rel="preload" as="image" href="/hero.jpg" fetchpriority="high"> in the head.
  2. Hero image is huge. A 2 MB JPEG forces a long download. Serve a properly sized WebP or AVIF, ideally under 100 KB above the fold.
  3. Render-blocking CSS in the head. Every external stylesheet blocks first paint, which delays LCP. Inline critical CSS or split out the non-critical rules.
  4. Slow server response. If TTFB is 2 seconds, LCP can't be better than 2 seconds. Fix the server before fixing the front end.
  5. Web fonts loading late. If your H1 uses a custom font, the browser waits for the font before painting the text. font-display: swap keeps the text visible.

The single biggest LCP improvement most sites can make is preloading the hero image and serving it in WebP at the right dimensions. That alone often moves a 4-second LCP to under 2 seconds.

INP: Interaction to Next Paint

INP is the new one. It replaced FID (First Input Delay) on March 12, 2024, after a year of being a "pending" metric. If you optimized for FID, your INP score may already be poor, because FID only measured input delay and INP measures the full interaction time.

What it measures: the time from a user interaction (click, tap, key press) to the next visual update on screen. Google looks at all interactions on the page and reports the 98th percentile.

| INP | Bucket | |---|---| | Under 200ms | Good | | 200ms to 500ms | Needs improvement | | Over 500ms | Poor |

What hurts INP:

  • Heavy JavaScript event handlers that block the main thread
  • Synchronous work in click handlers (database calls without async, large array operations)
  • Third-party scripts that hijack interactions (chat widgets, analytics, tag managers)
  • React/Vue components that re-render large trees on interaction
  • Long tasks (anything over 50ms blocks responsiveness)

What moves INP:

  1. Break up long tasks. Wrap synchronous work in requestIdleCallback or setTimeout(fn, 0) so the browser can paint between operations.
  2. Debounce input handlers. A search input that fires on every keystroke triggers an interaction event each time. Debounce by 100ms.
  3. Audit third parties. Tag managers and chat widgets often add 100-200ms to every click. Defer their initialization until after first interaction.
  4. Use the View Transitions API for navigation. Browser-native transitions feel instant.
  5. Move work to web workers. Heavy computation belongs off the main thread.

INP is the metric where a site can look great in a lab test and be terrible in the field. Real users have slower devices and more browser extensions than your laptop does.

CLS: Cumulative Layout Shift

What it measures: the sum of unexpected layout shifts that happen while the page is loading and during user interactions. A "shift" is when an element moves from one position to another between rendered frames.

| CLS | Bucket | |---|---| | Under 0.1 | Good | | 0.1 to 0.25 | Needs improvement | | Over 0.25 | Poor |

Common causes of bad CLS:

  • Images without explicit width and height attributes
  • Web fonts that load late and resize the text (FOUT)
  • Ads, embeds, and iframes that insert themselves above content
  • "Accept cookies" banners that push the page down a second after load
  • Lazy-loaded images that appear without reserved space

What moves CLS:

  1. Set width and height on every image and video. The browser reserves space before the asset loads. This single fix usually drops CLS by 0.1 or more.
  2. Reserve space for ads and embeds. Even if the ad is async, define a CSS min-height for its container.
  3. Use font-display: optional or preload fonts. Optional avoids the layout shift entirely. Preload reduces the swap delay.
  4. Show cookie banners as overlays, not push-down banners. A modal that floats above the content doesn't shift layout.

CLS is the easiest of the three to fix and the one that catches the most agencies off guard, because it doesn't always show in a quick local test. The shift happens when something async loads, and on a fast connection you might never see it.

TTFB: the foundation under LCP

TTFB (Time to First Byte) isn't officially a Core Web Vital, but it sits underneath LCP. If your server takes 1.5 seconds to respond, LCP can't be better than 1.5 seconds, full stop.

| TTFB | Bucket | |---|---| | Under 800ms | Good | | 800ms to 1800ms | Needs improvement | | Over 1800ms | Poor |

Common TTFB problems:

  • Shared hosting (your site shares CPU with a hundred others)
  • Database queries on every request (no caching)
  • Server-side rendering with cold edge starts
  • CDN cache misses

If TTFB is poor, no front-end fix will save your LCP. The fix is hosting, caching, or both.

How to actually measure your site

Three tools, three purposes:

  1. PageSpeed Insights for a one-page snapshot. Shows both lab (Lighthouse) and field (CrUX) data side by side.
  2. Google Search Console under the Core Web Vitals report for site-wide field data. Tells you which URLs are failing, grouped by similar issues.
  3. Real User Monitoring (RUM) if you want continuous field data and your own dashboards. The free web-vitals JS library by Google captures the metrics from your real visitors.

Don't optimize blindly. Look at field data first, find the URLs in the "Poor" bucket, then run Lighthouse on those URLs to see what changed.

What we check in an AcuityScan run

The performance module in a full AcuityScan run measures LCP, INP, CLS, and TTFB on the URL you provide, plus a handful of supporting metrics (Speed Index, Total Blocking Time, transfer size). The report flags which specific resources are slowing each metric and shows the fix priority. You can also run the performance tool on its own if you only need a Core Web Vitals snapshot.

If your real-user data in Search Console is poor but your lab score is fine, that's the gap RUM and field-data tools are built to close. Real users don't sit on your dev laptop.

Scan your own site

See what 350+ checks find on your domain.

Free, no signup, 60 seconds. Email auth · DNS · SSL · Performance · SEO · Accessibility · Privacy · Mobile.