GuideNext.jsastro

Lighthouse Performance Metrics Explained: What Each Score Means and How to Improve It

A plain-language breakdown of every Lighthouse metric — LCP, TBT, CLS, FCP, Speed Index — what causes poor scores, and specific fixes for each one.

M

MigrateLab Team

Migration Experts

8 min readApril 10, 2026
Lighthouse Performance Metrics Explained: What Each Score Means and How to Improve It

How Lighthouse Calculates Your Performance Score

Lighthouse is Google's open-source tool for auditing web page quality. It simulates a mid-tier mobile device (a Moto G Power on a throttled 4G connection) and measures how your page loads and becomes interactive. The result is a performance score from 0 to 100, calculated as a weighted average of five metrics.

The current Lighthouse 12 weights: LCP 25%, TBT 30%, CLS 25%, FCP 10%, Speed Index 10%. Each metric is scored on a log-normal distribution — the first improvements have the biggest impact on the final score, and the returns diminish as you approach the ideal threshold. This is why going from 60 to 80 is easier than going from 80 to 95.

Understanding these weights helps you prioritize. Improving a 30%-weighted metric (TBT) has 3x more impact on your overall score than improving a 10%-weighted metric (FCP). This guide walks through each metric in order of weight, with specific causes and fixes for each.

Total Blocking Time (TBT) — 30% of Score

TBT has the highest weight of any metric, yet it's the one most people pay the least attention to. TBT measures how long the main thread is blocked during page load — specifically, the cumulative time above 50ms for each 'long task.' If a task takes 200ms, it contributes 150ms to TBT (200 - 50 = 150).

Thresholds: Good: under 200ms. Needs improvement: 200-600ms. Poor: over 600ms. TBT is the lab equivalent of Interaction to Next Paint (INP), which Google uses in real-world field data for ranking decisions.

Why TBT Matters So Much

While the main thread is blocked, the browser literally cannot respond to user input. The user clicks a button — nothing happens. They try to scroll — it stutters. They tap a link — no response. This is the experience TBT quantifies: the total time during page load when your site is unresponsive.

What Causes High TBT

TBT is almost entirely caused by JavaScript. Every script your page loads must be parsed and executed, and during that time the browser can't do anything else. The most common culprits:

  • Large JavaScript bundles — more code means more parsing and execution time
  • Third-party scripts — analytics, chat widgets, tracking pixels, A/B testing tools
  • Synchronous JavaScript — scripts without async/defer attributes block the HTML parser
  • Expensive DOM operations — manipulating thousands of DOM nodes on page load
  • Heavy framework hydration — React/Vue/Angular re-attaching event listeners to server-rendered HTML

How to Fix TBT

The fundamental fix is reducing the amount of JavaScript that runs during page load. Specific strategies:

  • Reduce bundle size: audit dependencies, tree-shake unused code, use lighter alternatives (Preact instead of React for simple sites)
  • Defer non-critical scripts: anything that isn't needed for initial render should use async or defer attributes
  • Break long tasks: use scheduler.yield() (Chrome 129+) or setTimeout(0) to break expensive operations into smaller chunks
  • Move computation off-thread: use Web Workers for heavy processing like data transformation or search indexing
  • Consider Astro: ships zero JavaScript by default — only adds JS for interactive "islands," keeping TBT near zero

Largest Contentful Paint (LCP) — 25% of Score

LCP measures when the largest visible element in the viewport finishes rendering. This is the moment the user perceives the page as 'loaded' — even if other elements are still loading below the fold. The LCP element is usually a hero image, a large heading, or a video poster.

Thresholds: Good: under 2.5 seconds. Needs improvement: 2.5-4 seconds. Poor: over 4 seconds. LCP is one of the three Core Web Vitals that Google uses as an actual ranking signal.

The Four Phases of LCP

LCP is the sum of four sequential phases, and optimizing requires understanding which phase is your bottleneck:

  • Time to First Byte (TTFB): server processing + network latency. Target: under 800ms.
  • Resource load delay: time between TTFB and when the LCP resource starts loading. Caused by render-blocking resources or the browser not discovering the image early enough.
  • Resource load duration: how long the LCP resource takes to download. Affected by image size and connection speed.
  • Element render delay: time between the resource loading and the element actually appearing on screen. Caused by JavaScript that blocks rendering.

How to Fix LCP

  • Identify your LCP element: Chrome DevTools → Performance → record page load → look for the "LCP" marker
  • Preload the LCP resource: <link rel="preload" as="image" href="/hero.webp"> so the browser starts loading it immediately
  • Never lazy-load the LCP image: it should be the first image to load, with loading="eager" (default) and fetchpriority="high"
  • Optimize the image: compress, use WebP/AVIF, and serve responsive sizes with srcset
  • Reduce TTFB: use a CDN, enable server-side caching, or serve static HTML
  • Eliminate render-blocking resources: inline critical CSS, defer non-critical CSS and JavaScript

Cumulative Layout Shift (CLS) — 25% of Score

CLS measures visual stability — how much the page content shifts unexpectedly during loading. Every time a visible element moves position after it's been rendered, CLS increases. The metric captures the frustration of trying to click a link only to have it jump when an ad or image loads above it.

Thresholds: Good: under 0.1. Needs improvement: 0.1-0.25. Poor: over 0.25. CLS is also a Core Web Vital used for rankings. It's measured as the sum of individual layout shift scores (impact fraction × distance fraction) during the page's lifetime.

Common Causes of CLS

  • Images without dimensions: the browser reserves zero space, then shifts content when the image loads
  • Late-loading web fonts: text renders in a fallback font, then shifts when the custom font loads (different metrics)
  • Dynamically injected content: ads, cookie banners, notification bars, and "sign up for our newsletter" pop-ups that push content down
  • Client-side rendering: content rendered by JavaScript after the initial HTML, causing visible reflow
  • Third-party embeds: YouTube, Twitter, and other embeds that load with unknown dimensions

How to Fix CLS

  • Always set image dimensions: add width and height attributes to every <img> and <video> element. The browser uses these to calculate aspect ratio before loading.
  • Match fallback fonts: use a fallback font with similar metrics to your custom font. Tools like Fontaine can generate override descriptors. Or use font-display: optional to prevent shifting entirely.
  • Reserve space for dynamic content: use CSS min-height on containers for ads, embeds, and other late-loading content
  • Use CSS transform for animations: transforms don't trigger layout recalculation. Use transform: translateY() instead of top or margin-top
  • Position banners without pushing content: cookie banners and notification bars should use position: sticky or overlay positioning, not insert above page content

First Contentful Paint (FCP) — 10% of Score

FCP marks when the first piece of DOM content — text, image, SVG, or canvas — appears on screen. It's a perception metric: the user sees something happening instead of staring at a blank screen. FCP doesn't measure useful content (that's LCP), just the first visual response.

Thresholds: Good: under 1.8 seconds. Needs improvement: 1.8-3 seconds. Poor: over 3 seconds. While it only accounts for 10% of the score, a slow FCP usually indicates deep issues with server response time or render-blocking resources that will also affect LCP and Speed Index.

How to Fix FCP

FCP is blocked by two things: server response time (the browser is waiting for HTML) and render-blocking resources (the browser has HTML but can't paint because it's downloading CSS/JS). Fixing TTFB improves FCP directly. Inlining critical CSS and deferring non-critical resources lets the browser paint sooner.

One often-overlooked factor: DNS resolution. If your HTML is hosted on one domain but your CSS is on another (like a CDN without preconnect), the browser has to resolve a new domain before it can even start downloading CSS. Use <link rel="preconnect"> for any third-party origins that are critical for rendering.

Speed Index — 10% of Score

Speed Index measures how quickly the visible area of the page is visually populated. It's calculated by capturing video frames of the page load and measuring the percentage of final visual completion at each point in time. A page where the top half loads first and the bottom half loads a second later will have a worse Speed Index than a page where everything loads at once.

Thresholds: Good: under 3.4 seconds. Needs improvement: 3.4-5.8 seconds. Poor: over 5.8 seconds. Speed Index is largely a consequence of FCP and LCP — if both of those are fast, Speed Index will be fast too. The main thing that hurts Speed Index specifically is progressive rendering issues: loading the page skeleton late, rendering elements one at a time, or showing a loading spinner for too long.

Lab Data vs Field Data: Which Matters More?

PageSpeed Insights shows both lab data (from Lighthouse, at the bottom) and field data (from Chrome User Experience Report / CrUX, at the top). They serve different purposes:

  • Lab data is consistent and reproducible — same device, same connection, same conditions every time. Use it for debugging and identifying specific issues. The main limitation: it tests one configuration that may not represent your actual users.
  • Field data shows how real users actually experience your site across different devices, connections, and locations. It's noisy (varies by user) but truthful. Google uses field data (Core Web Vitals) for ranking decisions, not lab scores.

If your lab scores are good but field data is poor, it usually means your site performs well on fast connections but poorly on the slower connections and lower-end devices that many real users have. The typical fix: reduce JavaScript payload (the #1 factor that hurts more on slower devices) and serve optimized images for mobile.

If your field data is good but lab scores are mediocre, you're probably fine for SEO — the field data is what Google uses. But the lab scores suggest there's room for improvement that would benefit users on slower connections.

Lighthouse vs PageSpeed Insights vs Chrome DevTools

These three tools all use Lighthouse under the hood, but with different configurations:

  • Chrome DevTools Lighthouse: runs locally in your browser. Uses your actual network connection and CPU. Results depend on your machine and current conditions.
  • PageSpeed Insights: runs on Google's servers with standardized throttling. Consistent results, plus shows field data from CrUX. This is the benchmark most people use.
  • Lighthouse CI: runs in CI/CD pipelines on your build server. Great for catching regressions before deployment. No field data.

For tracking performance over time, use PageSpeed Insights or Lighthouse CI (consistent conditions). For debugging specific issues, use Chrome DevTools (full access to Network, Performance, and Coverage panels). For understanding real-world impact, use Google Search Console's Core Web Vitals report.

Score Variability: Why Your Score Changes Between Runs

Lighthouse scores can vary by 5-15 points between runs, even for the same page. This is normal and caused by:

  • Local network conditions (when running in DevTools)
  • Server response time variability
  • Third-party script timing (ad scripts, analytics, chat widgets load at different speeds)
  • Browser extension interference (when running locally)
  • Background processes on your machine consuming CPU

Best practice: run Lighthouse 3-5 times and take the median score. Or use tools like Unlighthouse which run multiple passes automatically. Don't make optimization decisions based on a single run's score — a 3-point difference might just be noise.

FeatureMetricGood Threshold
Total Blocking Time (TBT)30% weight — main thread blocking during loadUnder 200ms
Largest Contentful Paint (LCP)25% weight — when main content finishes renderingUnder 2.5 seconds
Cumulative Layout Shift (CLS)25% weight — unexpected visual movementUnder 0.1
First Contentful Paint (FCP)10% weight — first content appearsUnder 1.8 seconds
Speed Index10% weight — visual population speedUnder 3.4 seconds