Performance #8 - Core Web Vitals & The Full Metrics Stack

LCP, CLS, INP are the ranking signals — but TTFB, FCP, TBT, TTI, and Speed Index explain why they fail. This covers all web vitals: what each measures, how they relate, and how to fix them.

12 min read
Performance
Web Vitals
Metrics

TABLE OF CONTENTS
Performance #8 - Core Web Vitals & The Full Metrics Stack

Three numbers sit at the top of Google's ranking signal: LCP, CLS, INP. But web performance has a broader vocabulary — TTFB, FCP, TBT, TTI, Speed Index, FID. This article maps all of them: what each one measures, where it lives in the loading timeline, how they connect to each other, and which ones actually move rankings.


The Two Tiers

Google's Web Vitals programme divides metrics into two tiers:

  • Core Web Vitals (CWV) — LCP, CLS, INP. These are Google's ranking signals. A "passing" site needs "Good" scores at the 75th percentile of field data for all three.
  • Other Web Vitals — TTFB, FCP, TBT, TTI, Speed Index. Diagnostic metrics — not ranking signals, but essential for understanding why your Core Web Vitals scores are what they are.

Core Web Vitals

Largest Contentful Paint (LCP)

What it measures: The render time of the largest visible content element — typically a hero image, a large text block, or a video poster. It marks the point when the user perceives the main content as loaded.

Threshold:

ScoreValue
Good≤ 2.5s
Needs improvement2.5s – 4s
Poor> 4s

LCP is dynamic — the "largest" element can change during page load. The browser might first paint a large <h1> as the LCP candidate, then a hero image finishes loading and becomes the new (final) LCP element. The LCP score is always the time of the final candidate, so a late-loading hero image pushes your LCP out.

Elements considered for LCP:

  • <img> elements
  • <image> inside <svg>
  • <video> poster images
  • Elements with background-image: url() (not CSS gradients)
  • Block-level text nodes (headings, paragraphs)

What degrades LCP:

  • Slow server response (high TTFB)
  • Render-blocking CSS or JavaScript delaying paint
  • LCP image not preloaded or accidentally lazy-loaded
  • Images served without width/height (causing layout recalculations)

How to improve LCP:

Other high-impact fixes: serve images in WebP/AVIF, reduce TTFB by serving from a CDN, and eliminate render-blocking resources from the critical path.


Cumulative Layout Shift (CLS)

What it measures: The sum of all unexpected layout shift scores during the page's lifespan. A layout shift occurs when a visible element changes its position on screen without user input.

Threshold:

ScoreValue
Good≤ 0.1
Needs improvement0.1 – 0.25
Poor> 0.25

The score is calculated per shift event as: impact fraction × distance fraction.

  • Impact fraction — what percentage of the viewport area was affected by the shift?
  • Distance fraction — how far did the element move relative to the viewport height?

CLS is measured from navigation until page unload — a layout shift from a late-loading ad, cookie banner, or toast notification 30 seconds after load counts just as much as one during the initial paint.

Common causes of CLS:

  • Images without width and height attributes — browser can't reserve space before the image loads
  • Ads, embeds, or iframes without reserved dimensions
  • Web fonts causing text to reflow when they load (FOUT) — see Font Optimization
  • Dynamically injected content above existing content (banners, cookie notices)
  • Animations that change layout properties instead of using transform

How to improve CLS:

For font-induced layout shift, font-display: swap with a well-matched system font fallback is the best balance for content sites. For ads and embeds, reserve space with a fixed min-height on the container.


Interaction to Next Paint (INP)

What it measures: The latency of the worst interaction (click, tap, or keypress) during a page session — specifically, the time from when the user interacts to when the browser produces the next visual update in response.

INP replaced First Input Delay (FID) as a Core Web Vital in March 2024. FID only measured the first interaction and only the queueing delay before processing; INP measures all interactions end-to-end (queueing + processing + rendering).

Threshold:

ScoreValue
Good≤ 200ms
Needs improvement200ms – 500ms
Poor> 500ms

What makes INP high:

  • Long tasks on the main thread blocking response to input
  • Heavy JavaScript execution in event handlers
  • Complex DOM updates triggered by interaction

How to improve INP:

INP in the field ≈ TBT in the lab. Since you can't simulate real user interactions in Lighthouse, look at Total Blocking Time (TBT) as your lab-side proxy. A high TBT means the main thread is busy, which means interactions will queue up and feel slow. Reducing long tasks improves both.


Other Web Vitals — Diagnostic Metrics

These are not ranking signals, but they explain why Core Web Vitals fail. Think of them as the layer beneath.


TTFB — Time to First Byte

What it measures: Everything between sending a request and receiving the first byte of the response: DNS lookup, TCP connection, TLS handshake, and server processing time.

Target: < 800ms

Why it matters: TTFB is the gate every other metric waits behind. A 500ms TTFB pushes LCP, FCP, and TBT out by the same amount regardless of any client-side optimisation. It's almost entirely a server/network problem — slow database queries, no CDN, cold serverless starts, or missing HTTP/2.

See What is TTFB? for a full breakdown.


FCP — First Contentful Paint

What it measures: The time at which the browser first renders any content — text, image, SVG, or non-white canvas. It's the first visual signal that the page is alive.

Threshold:

ScoreValue
Good≤ 1.8s
Needs improvement1.8s – 3s
Poor> 3s

FCP ≤ LCP always — FCP fires when the first thing appears; LCP fires when the largest thing appears. A large gap between the two usually means the main content (a hero image) is being discovered and downloaded well after the initial paint — a sign you need to preload it.

See What is FCP? for a full breakdown.


TBT — Total Blocking Time

What it measures: The sum of the "blocking portion" of every long task between FCP and TTI. A task is "blocking" if it exceeds 50ms — TBT counts the milliseconds above 50ms for each one.

Long task A: 250ms  →  blocking portion = 250 - 50 = 200ms
Long task B:  80ms  →  blocking portion =  80 - 50 =  30ms
                                          TBT = 230ms

Threshold:

ScoreValue
Good≤ 200ms
Needs improvement200ms – 600ms
Poor> 600ms

TBT is lab-only — you can't measure it from real user sessions because it requires the full task timeline. It's the primary lab-side proxy for INP: reduce TBT and you almost always reduce INP.

How to reduce TBT:

  • Break long tasks with scheduler.yield() or setTimeout(..., 0)
  • Defer non-critical JavaScript (defer, async, or load-on-interaction)
  • Move heavy computation to Web Workers
  • Reduce hydration cost in SSR apps with streaming or selective hydration

TTI — Time to Interactive

What it measures: The earliest point at which the page is both visually usable and reliably interactive — specifically, the first 5-second window after FCP where no long task takes more than 50ms and the network is quiet (≤ 2 in-flight requests).

Target: < 3.8s (lab data)

Why it matters: TTI exposes apps that look loaded but aren't. A React app that renders a shell immediately but blocks the thread during hydration will have a low LCP but a high TTI — the gap between the two is the "uncanny valley" where users click buttons and nothing happens.

TTI has been de-emphasised by Google since INP was introduced, but it remains useful for diagnosing hydration-heavy SPA pages.


Speed Index (SI)

What it measures: How quickly content is visually populated during page load — not just when it finishes, but how progressively it fills in. It's computed by analysing video frames of the loading process.

Threshold:

ScoreValue
Good≤ 3.4s
Needs improvement3.4s – 5.8s
Poor> 5.8s

A page that holds a blank white screen until everything is ready will have a high Speed Index even if its LCP is acceptable. SI rewards progressive rendering — content that fills in steadily rather than appearing all at once.

How to improve SI:

  • Inline critical CSS so above-the-fold content paints first
  • Eliminate render-blocking resources from <head>
  • Prioritise above-the-fold content in SSR output
  • Use skeleton screens or progressive image loading (blur-up)

FID — First Input Delay (Deprecated)

Historical context: FID was a Core Web Vital from 2020 until March 2024. It measured only the queueing delay before the browser began processing the first user interaction — not the time to actually paint a response.

Why it was replaced: FID had two key weaknesses — it only measured the first interaction, and it only measured the queueing delay (not the total processing + rendering time). INP fixes both.

If you see FID in older audits or dashboards, treat it as a rough signal for main-thread contention during initial load. The fixes are identical to INP.


How the Metrics Relate

Navigation starts
│
├─ [TTFB] ─────── server response time (the gate)
│
├─ [FCP] ──────── first anything painted
│    │
│    ├─ [LCP] ─── largest element painted        ┐
│    ├─ [CLS] ─── layout shifts (whole session)  ├─ Core Web Vitals
│    └─ [INP] ─── worst interaction (field only) ┘
│
├─ [TBT] ──────── lab proxy for INP (FCP → TTI)
├─ [TTI] ──────── first 5s quiet window
└─ [SI] ───────── progressive fill score

The cascade in practice:

  • High TTFB → everything else is late
  • High TBT → high INP (main thread is too busy to respond)
  • Missing image dimensions → high CLS
  • Late-discovered hero image → high LCP (and large LCP–FCP gap)

Field Data vs Lab Data

MetricField (RUM)Lab (Lighthouse)
LCP
CLS
INP✅ only❌ use TBT
TTFB
FCP
TBT❌ use INP✅ only
TTI
Speed Index

Field data (CrUX, PageSpeed Insights, Search Console) is what Google uses for ranking. Lab data (Lighthouse, WebPageTest) is for diagnosis and local iteration.

The critical rule: a site must pass CWV thresholds at the 75th percentile of field data to receive the ranking signal. Lab scores don't count for ranking — they count for debugging.


Prioritising Fixes

PriorityMetricIf it's bad…
1TTFBAdd CDN, fix server response time, enable HTTP/2
2LCPPreload hero image, serve WebP/AVIF, avoid lazy-loading it
3CLSAdd explicit image dimensions, reserve space for ads and fonts
4TBT / INPBreak up long tasks, defer non-critical JS, use Web Workers
5FCPInline critical CSS, remove render-blocking resources
6TTI / SICode-split, reduce JS parse cost, defer hydration

Start with TTFB — it's a multiplier. A 500ms improvement in TTFB improves LCP, FCP, and TBT simultaneously without touching a single line of front-end code.


Where to Go Next


Let's Connect

© 2026 Naveen Karthik // Built with React & MUI