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:

MetricMeasuresUsually caused by
LCPWhen the largest element paintsRender-blocking resources, unoptimised hero images
INPResponsiveness to interactionLong JavaScript tasks on the main thread
CLSUnexpected layout movementImages 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 defer on scripts, or type="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-adjust on 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.

  • width and height on every image and iframe. The browser computes the aspect ratio and holds the space.
  • A fixed min-height on 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.

Related

Next step

Working on something this touches?

If this post lines up with a problem you have, I would rather hear about the problem than the post. Tell me what you are building.

  • Replies: Within two working days
  • Based in: the United States
  • Open to: full time, contract, remote or hybrid

Pages

Projects

Writing

Actions