Diagnose LCP, CLS and INP problems with Lighthouse and Chrome DevTools.
Why it matters
Users judge a site in the first few seconds: did the main content show up quickly, did the page jump around, did the button respond when I tapped it? Google measures exactly those three things as the Core Web Vitals, uses them as a search ranking signal, and reports them for real visitors in the Chrome User Experience Report. On a front-end team, "LCP regressed on the course page" is a normal ticket, and you are expected to know how to find the cause.
The three metrics
| Metric | What it measures | Good (75th percentile of visits) |
|---|---|---|
| LCP, Largest Contentful Paint | When the largest image or text block in the viewport finished rendering | 2.5 s or less |
| CLS, Cumulative Layout Shift | How much visible content moved unexpectedly (a unitless score) | 0.1 or less |
| INP, Interaction to Next Paint | The delay between a click, tap or key press and the next frame the user sees, near the worst of the visit | 200 ms or less |
INP replaced First Input Delay (FID) as a Core Web Vital in March 2024. Thresholds and guidance live at web.dev/vitals (checked on 2026-10-23).
Lab data and field data
- Lab data comes from a controlled test, such as Lighthouse in Chrome DevTools. It is repeatable and great for debugging, but it uses one simulated device and network.
- Field data (also called RUM, real user monitoring) comes from real visitors. PageSpeed Insights shows it for popular sites, and you can collect your own with Google's small
web-vitalslibrary.
Lab LCP and CLS approximate field values. INP needs real interactions, so Lighthouse reports Total Blocking Time (TBT) as a lab proxy: long JavaScript tasks that block the main thread.
Diagnosing LCP
LCP time breaks into four parts: server response (TTFB), the delay before the browser discovers the LCP resource, the time to download it, and the time to render it. Common fixes:
- The hero image is discovered late (set as a CSS background, or injected by JavaScript). Use a normal
<img>in the HTML, and addfetchpriority="high"to it. - The hero image has
loading="lazy". Never lazy-load the LCP image. - The image is far too large. Serve a sized, modern-format image (next lesson).
- Render-blocking CSS or fonts delay the first paint. Inline critical CSS, defer the rest, use
font-display: swap. - The page is rendered entirely on the client, so nothing shows until JavaScript downloads and runs (see the rendering strategies lesson).
Diagnosing CLS
Layout shifts happen when something appears or resizes and pushes visible content. Common causes and fixes:
<!-- Bad: the browser does not know the height until the image loads -->
<img src="/hero.jpg" alt="Students at a workshop">
<!-- Good: width/height (or CSS aspect-ratio) reserve the space -->
<img src="/hero.jpg" alt="Students at a workshop" width="1200" height="600">
- Images, videos and iframes without dimensions.
- Banners, ads or cookie notices inserted above existing content. Reserve space or overlay them.
- Skeletons that are a different height from the loaded content.
- Web fonts swapping in with different metrics. Use
size-adjustor a close fallback font.
Shifts within 500 ms of a user's input do not count, so content that expands because the user clicked is fine.
Diagnosing INP
Slow interactions come from too much work on the main thread between the input and the next paint:
- A click handler that does heavy synchronous work (sorting thousands of items, big JSON parsing).
- A state change that re-renders a huge React tree.
- Long tasks from third-party scripts running when the user taps.
Fixes: do less work per interaction, show feedback first and defer the rest (await a yield such as scheduler.yield() where supported, or setTimeout), virtualise long lists, memoise expensive subtrees, and move heavy computation to a Web Worker. In the DevTools Performance panel, record an interaction and look for long tasks (red corners, over 50 ms) under it.
Worked example: reading a Lighthouse report
You run Lighthouse (DevTools > Lighthouse > Mobile > Performance) on a course page and get LCP 4.8 s, CLS 0.27, TBT 640 ms.
- Expand Largest Contentful Paint element: it is the hero
<img>. The LCP breakdown shows most time in "load delay", and the image hasloading="lazy". Fix: remove lazy loading, addfetchpriority="high". - Expand Avoid large layout shifts: the shifting element is the course grid, pushed down when a promo banner loads. Fix: reserve the banner's height with
min-height. - Under Reduce JavaScript execution time, a charting library parses on every page even though the chart is below the fold. Fix: load it on demand (next lesson).
Re-run the audit after each fix and compare. Change one thing at a time so you know what helped.
Common mistakes
- Chasing a 100 score. The score is a summary; the metrics and real-user data matter more.
- Testing only on a fast laptop. Use the mobile preset and CPU throttling, and check field data.
- Running Lighthouse with extensions on. Use an incognito or guest window so extensions do not skew results.
- Fixing without measuring. Record a baseline, change one thing, measure again.
Quick recap
- LCP (loading, 2.5 s), CLS (stability, 0.1) and INP (responsiveness, 200 ms), measured at the 75th percentile.
- Lab tools debug; field data tells you what users actually experience.
- LCP: make the hero discoverable, prioritised and small. CLS: reserve space. INP: keep the main thread free during interactions.
Try it
The Do tab walks you through auditing a real page with Lighthouse and the Performance panel and writing up one fix for each metric.