Every performance engagement starts the same way: someone shares a Lighthouse report with a red number on it and asks us to make it green. We can usually do that in a day. It rarely makes the site feel faster, because Core Web Vitals measure symptoms, not causes.
Three real examples
LCP of 4.1s on a marketing site. The obvious read is "the hero image is too big." It was 90KB — fine. The actual cause was a font stack loading from two origins, blocking text render for 1.8s. Self-hosting the two weights we actually used took LCP to 1.4s and we never touched the image.
CLS of 0.31 on a product listing. Not images without dimensions — a cookie banner injecting itself above the fold 600ms after paint. Reserving its height fixed the score. Moving it to the bottom fixed the experience.
INP of 480ms on a filter UI. Every keystroke re-rendered 200 cards. Debouncing helped the metric. Virtualising the list and moving filtering off the main thread is what made it feel instant.
The pattern
In all three cases the metric pointed at a region of the page, not a cause. The work was diagnosis. If you optimise for the number without doing that, you get a green dashboard and users who still bounce.
How we sequence it
Field data first — real users on real devices, not a lab run on a fast laptop. Then profile the worst percentile, not the median. Then fix causes in order of user impact, and let the scores follow. They always do.
- #Performance
- #Core Web Vitals
- #Next.js
- #Frontend