Most performance advice is a long list where every item sounds equally important. In practice a few changes produce nearly all of the improvement, and the rest is polish.
Here is the order I work in, with the reasoning for each.
First, know which metric you are failing
The three Core Web Vitals measure different things and have different fixes:
| Metric | Measures | Usually caused by |
|---|---|---|
| LCP | When the largest element paints | Render-blocking resources, unoptimised hero images |
| INP | Responsiveness to interaction | Long JavaScript tasks on the main thread |
| CLS | Unexpected layout movement | Images and ads without reserved space, late fonts |
Fixing CLS will not help an LCP problem. Measure first, using field data if you have it, because lab scores on a fast laptop flatter everybody.
1. Find the LCP element and make it arrive first
Open DevTools, run a Lighthouse pass, and read which element it names. It is almost always a hero image or a headline.
If it is an image:
<img
src="/hero-960.avif"
srcset="/hero-640.avif 640w, /hero-960.avif 960w, /hero-1440.avif 1440w"
sizes="(min-width: 64rem) 960px, 100vw"
width="960"
height="540"
fetchpriority="high"
alt="..."
>
Four things are doing work there. Modern format, so the bytes are a fraction of
the JPEG. srcset and sizes, so a phone does not download a desktop image.
Explicit width and height, which reserves the space and protects CLS.
fetchpriority="high", which tells the browser this one matters.
Never lazy-load the LCP image. loading="lazy" on your hero delays the exact
thing you are being measured on.
2. Remove render-blocking resources
Every stylesheet in the head blocks the first paint. Every synchronous script blocks parsing.
- Inline the critical CSS, or on a small site inline all of it. This site’s entire stylesheet ships inside the HTML, which removes a round trip.
- Put
deferon scripts, ortype="module", which defers by default. - Question every third-party script. A tag manager, a chat widget, and an analytics script together can cost more than your whole page.
3. Fix your fonts, or skip them
Web fonts are the most common source of both LCP and CLS problems on otherwise clean sites.
The cheapest fix is to not load one. A system font stack costs zero bytes, renders instantly, and looks native:
font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
If you do need a custom face:
- Self-host it. A third-party font host is a second connection before any text can paint.
- Subset it to the characters you use.
font-display: swap, so text is readable immediately.- Set
size-adjuston the fallback so the swap does not shift the layout.
4. Reserve space for everything
CLS is the easiest of the three to get to zero, and the one most often left broken.
widthandheighton every image and iframe. The browser computes the aspect ratio and holds the space.- A fixed
min-heighton anything that loads late: embeds, ads, dynamic banners. - Never insert content above existing content after load. A cookie bar that pushes the page down is a CLS failure and an annoyance in one.
5. Cut the JavaScript
INP is about the main thread. The fastest JavaScript is the JavaScript you did not ship.
Ask what each bundle is for. A marketing page rarely needs a client-side framework at all. If you are reaching for React to render static content, you are paying hydration cost for nothing.
Where you do need interactivity, keep handlers short. Anything over about 50ms blocks the next interaction. Break long work up, or move it off the main thread.
6. Cache properly
Content-hashed filenames can be cached forever:
/assets/*
Cache-Control: public, max-age=31536000, immutable
HTML should not be. A short max-age with revalidation means a deploy is visible immediately.
A realistic budget
For a content or marketing site, aim for:
- Under 100 KB of JavaScript, compressed. Under 50 KB is very achievable.
- Under 30 KB of CSS, compressed.
- LCP image under 150 KB.
- Zero layout shift. Not “small”, zero.
These are not aspirational. This site comes in well under all of them, and it is not doing anything unusual to get there.
What I skip
Some advice returns very little for the effort on most sites: preloading everything (which just moves the queue around), micro-optimising a critical CSS extraction on a site whose CSS is 8 KB, and chasing the last two points of a lab score when field data already looks fine.
Do the first five items properly and the sixth is rarely necessary.