Core Web Vitals Without the Headache
The four front-end decisions that move LCP, INP, and CLS more than any amount of micro-optimisation — with the measurements that tell you whether they worked.
CodeDevlify Editorial
Product & Platform team
Most performance advice is a list of forty things. In practice four decisions account for nearly all of the movement, and the rest is noise until those four are right.
| Metric | What it measures | Good | Usual culprit |
|---|---|---|---|
| LCP | Time to the largest visible element | ≤ 2.5s | An unoptimised hero image |
| INP | Responsiveness to input | ≤ 200ms | Too much JavaScript on the main thread |
| CLS | Unexpected layout movement | ≤ 0.1 | Images and ads without reserved space |
1. Give every image a size
This is the cheapest fix in web performance and it lands on two metrics at once: the browser reserves the box (CLS) and can prioritise the right file (LCP). If you're on Next.js, next/image does it for you as long as you actually pass the dimensions.
<Image src="/blog/hero.jpg" alt="A dashboard showing site analytics" width={1200} height={630} priority // only on the LCP image sizes="(min-width: 1024px) 720px, 100vw" // stops over-large downloads on phones/>2. Load fonts without a flash
A web font that arrives after first paint reflows your entire text column. Self-host the font, subset it to the characters you use, and set font-display: swap with a metrics-matched fallback so the swap doesn't shift anything.
@font-face { font-family: "Inter"; src: url("/fonts/inter-subset.woff2") format("woff2"); font-display: swap; /* Matched to the fallback so swapping in doesn't move a single line. */ size-adjust: 107%; ascent-override: 90%;}3. Ship less JavaScript to the client
INP is almost entirely a main-thread problem. The fix is not faster code; it's absent code. Render on the server, and let only genuinely interactive leaves become client components — a copy button, a filter, a menu.
- Push
"use client"as far down the tree as it will go — a client component drags its whole subtree with it. - Import icons and utilities individually so the bundler can drop the rest.
- Defer third-party scripts (chat widgets, analytics) to
lazyOnload; none of them need to run before your content does.
4. Measure the field, not your laptop
Lighthouse on a wired connection is a debugging tool, not a score. What ranks is field data from real devices. Run the lab test to find the cause, but confirm the fix against real users.
# Lab: reproducible, good for A/B-ing a changenpx unlighthouse --site https://example.com # Field: what actually counts# PageSpeed Insights → "Discover what your real users are experiencing"