How to Pass Core Web Vitals: A Practical Guide
If your Lighthouse score is 98 and Search Console still says your pages need improvement, nothing is broken. You're reading two different measurements, and only one of them counts.
We've covered why speed is a growth lever. This is the practical follow-up: how to actually pass.
First, the thing most teams get wrong
Lighthouse is a lab test. Core Web Vitals are field data.
Lighthouse runs on a simulated device on your machine, on demand. Core Web Vitals come from real Chrome users on real devices and real networks, aggregated over a rolling 28-day window, and you're assessed at the 75th percentile — meaning three-quarters of visits must hit the target.
Three consequences that explain most confusion:
- Your average doesn't matter. You must fix the slow quarter. A fast median with a slow tail still fails. Optimising for your own fast laptop is optimising the wrong end of the distribution.
- Fixes take weeks to appear. Deploy today and the 28-day window still contains four weeks of old data. Nothing is wrong — wait.
- Lab tools are for diagnosis, field data is for grading. Use Lighthouse to find causes, not to decide whether you've passed.
The targets, at the 75th percentile: LCP under 2.5s, INP under 200ms, CLS under 0.1.
LCP: what's the element, then which phase?
Largest Contentful Paint measures when the main content appears. Two steps, in order.
Step one: identify the element. Usually the hero image or the headline. Every dev-tools performance panel will name it. Don't guess — teams routinely optimise an image that isn't the LCP element.
Step two: work out which phase is slow. LCP breaks into four, and the fix depends entirely on which dominates:
| Phase | What it means | Typical fix |
|---|---|---|
| Time to first byte | Server was slow to respond | Caching, CDN, faster queries |
| Resource load delay | Discovery was late | Preload it, stop lazy-loading it |
| Resource load time | The file is too big | Modern formats, right dimensions |
| Element render delay | Blocked by other work | Cut render-blocking CSS/JS |
The most common single mistake: lazy-loading the LCP image. It's an easy blanket setting to apply, and on the hero image it directly delays the metric you're being graded on. Anything above the fold should load eagerly, and the hero should usually be preloaded.
Second most common: a hero that waits on JavaScript to render. If the largest element only exists after hydration, LCP includes all of that. Render it server-side.
INP: stop blocking the main thread
Interaction to Next Paint measures how quickly the page responds when someone taps or clicks. It replaced FID with a much stricter definition — it measures every interaction, not just the first, and covers the full path to the next visual update.
Almost always the cause is too much JavaScript on the main thread:
- Long tasks. Any single task over 50ms delays every interaction landing during it. Break work into smaller pieces and yield between them.
- Hydration cost. A page that hydrates a large interactive tree is unresponsive while it does so, no matter how quickly it painted.
- Expensive event handlers. Work done synchronously in a click or input handler before painting. Update the UI first, then do the work.
- Third-party scripts. Chat widgets, tag managers, A/B testing tools. Audit these honestly — they're often the largest contributor and the least examined.
The structural fix is shipping less JavaScript: render on the server, and hydrate only what genuinely needs interactivity.
CLS: reserve the space
Cumulative Layout Shift measures unexpected movement. It's usually the easiest to fix, and it comes from a short list:
- Images and video without dimensions. Set explicit
widthandheight(or an aspect ratio) so the browser reserves space before the file arrives. This alone fixes most CLS. - Ads, embeds, and iframes with no reserved container.
- Banners injected at the top of the page — cookie notices especially. Overlay them, or reserve their height.
- Fonts. A fallback with different metrics reflows text when the web font loads. Use
font-display: swapwith a metric-matched fallback, or preload the font. - Content inserted above existing content after load. If you must, insert below the fold or reserve the space.
Note that CLS keeps accumulating for the whole page lifetime, not just at load. A shift caused by lazy content halfway down still counts.
The order to work in
- Get field data. Search Console's Core Web Vitals report, grouped by URL pattern. Fix the pattern affecting the most pages first.
- Fix CLS. Cheapest, most mechanical, fastest to verify.
- Fix LCP. Identify the element, find the dominant phase, fix that phase only.
- Fix INP. Usually the hardest, because it means shipping less JavaScript rather than configuring something.
- Wait. Four weeks minimum before judging whether it worked.
Test on a mid-range Android phone on a throttled connection, not your laptop. That's much closer to the 75th percentile of your real traffic, which is the number you're being graded on.
Build it in rather than bolting it on
Retrofitting performance is expensive and rarely gets all the way there. Server-first rendering, disciplined JavaScript, and images sized properly from the start cost almost nothing during a build and are painful to add afterwards — the same argument as everything the cheap quote leaves out.
If you'd like a diagnosis of which metric is actually failing and why, rather than a generic audit, that's work we do. See how we approach web development, or get in touch.
Ready to build it? Let's talk about your project.
Web & E-Commerce →