Skip to content
brainNotFound

Engineering

A Core Web Vitals Budget You Can Actually Ship

Concrete numbers, the handful of techniques that move them, and how to stop a fast site from slowly getting slow.

VoidReturn2 min read
Vertical bar spectrum over a dark grid, labelled 204 NO CONTENT

Performance work fails for a boring reason: nobody wrote down the number. "Make it fast" is not a budget. These are, and they are the ones I put in writing before a project starts.

MetricBudgetFails when
LCP< 2.5sHero image is lazy, or fonts block paint
CLS< 0.1Images without dimensions; late-loading banners
INP< 200msHydrating components nobody interacts with
JS shipped< 120kB gzipA client component in the layout
The budget, measured on mobile

LCP is almost always one image

On a content site the largest contentful paint is the hero. Three things fix it and they are not negotiable: mark it priority so it is preloaded rather than lazy, give it explicit dimensions, and serve AVIF.

components/Hero.tsxtsx
<Image
  src={cover.src}
  alt={cover.alt}
  width={cover.width}
  height={cover.height}
  placeholder="blur"
  blurDataURL={cover.blurDataURL}
  priority
  sizes="100vw"
/>

Everything below the fold gets the opposite treatment: lazy by default, with a blur placeholder so the space is reserved and the page never jumps.

CLS is a discipline, not a fix

Layout shift comes from four places, and all four are preventable:

  1. Images without dimensions. Project width and height from the CMS and pass them through.
  2. Fonts. Self-host with next/font, use display: swap, and let it generate the metric-matched fallback.
  3. Late content. Anything that appears after hydration must occupy its space beforehand.
  4. Animation. Animate transform and opacity only. Animating height or margin is a shift with extra steps.

INP is a JavaScript problem

Interaction latency on a content site is nearly always self-inflicted: a client component high in the tree that hydrates the entire page. The fix is architectural rather than clever.

Keep layouts and pages as server components. Push "use client" down to the smallest leaf that genuinely needs state — a menu toggle, a scroll reveal, a counter. Everything else stays HTML.

Bar chart spectrum representing a performance budget holding steady over time
The budget only means something if it is checked on every deploy.

Stopping the slow slide

Fast sites get slow one reasonable-sounding addition at a time. Two habits keep it honest: run Lighthouse on the same three URLs — home, an article, a case study — every time, and treat a regression as a bug with an owner, not a note in a backlog.

The motion layer is usually the first thing to blame and rarely the actual culprit. Scroll reveals gated behind prefers-reduced-motion and restricted to transform and opacity cost almost nothing; a marketing script costs everything.

// related

Keep reading.

All Engineering