web-design

Core Web Vitals 2026: a practical implementation guide

By Justin
CORE WEB VITALS 1.1s LCP GOOD · < 2.5s 90ms INP GOOD · < 200ms 0.02 CLS GOOD · < 0.1

Core Web Vitals have been a ranking factor for almost five years now and a UX baseline even longer. Yet most sites we audit still fail at least one of the three. Not because the targets are hard to hit — they’re not — but because the work is unglamorous and gets de-prioritized below shinier projects.

Here’s what the current metrics measure, where typical sites fail, and the implementation patterns that consistently pass.

The three metrics in plain language

LCP — Largest Contentful Paint

How fast the biggest visible element renders. For most marketing sites, this is the hero image or the H1 paragraph. Target: under 2.5 seconds. Excellent: under 1.5 seconds.

CLS — Cumulative Layout Shift

How much things move around as the page loads. Buttons that shift right as ads load. Content that jumps when fonts swap. Target: under 0.1. Excellent: under 0.05.

INP — Interaction to Next Paint

How long it takes the page to visibly respond when a user taps or clicks something. Replaced FID in 2024. Target: under 200ms. Excellent: under 100ms.

These three measure the user-perceived performance of your site. Not synthetic lab tests — real users on real devices on real connections.

Where typical sites lose points

1. Heavy hero images, served at the wrong size

A 3MB hero image at 2400×1600 served to a phone is the single most common LCP killer we see. Fix: serve the right size for the viewport, in AVIF or WebP, with proper srcset and sizes attributes. Add fetchpriority="high" to your hero image so it doesn’t wait behind less important resources.

2. Custom fonts loaded without fallback

Fonts loaded from fonts.googleapis.com without font-display: swap cause your text to be invisible until the font loads. That hurts LCP if the largest element is text. Fix: always use font-display: swap. Better: self-host fonts and preload the one used for above-the-fold text.

3. JavaScript blocking the main thread

Big JS bundles parsing on the main thread before anything renders. Common with traditional React SPAs, certain WordPress themes, and most tag-manager-heavy setups. Fix: defer non-critical scripts, code-split aggressively, and consider whether you need a SPA at all (most marketing sites don’t).

4. Layout shift from ads, embeds, and lazy images

Lazy-loaded images without width and height attributes cause shift when they pop in. Ads inserted after page load push everything down. Fix: always specify image dimensions, reserve space for ads and embeds via CSS aspect-ratio, never inject content above existing content.

5. Hydration cost on interactive sites

Interactive sites built with full SPA hydration pay a one-time INP cost on initial load that’s often hundreds of ms. Fix: use partial or selective hydration. Astro’s islands architecture is built for this. Next.js’s Server Components solve a related problem.

The three thresholds, visualized

LCP · Largest Contentful PaintGOOD · < 2.5sNEEDS WORKPOOR · > 4sINP · Interaction to Next PaintGOOD · < 200msNEEDS WORKPOOR · > 500msCLS · Cumulative Layout ShiftGOOD · < 0.1NEEDS WORKPOOR · > 0.25
Google’s official thresholds for “Good” vs “Needs Improvement” vs “Poor” on each metric.

Implementation patterns that pass

Hero image, done right

<img
  src="/hero.avif"
  srcset="
    /hero-640.avif 640w,
    /hero-1024.avif 1024w,
    /hero-1920.avif 1920w
  "
  sizes="(max-width: 768px) 100vw, 50vw"
  alt="Product screenshot"
  width="1920"
  height="1080"
  fetchpriority="high"
  decoding="async"
/>

The combination of fetchpriority="high", explicit dimensions, modern formats, and proper srcset will get most sites under a 1.5s LCP without any other changes.

Font loading, done right

<link
  rel="preload"
  href="/fonts/inter-var.woff2"
  as="font"
  type="font/woff2"
  crossorigin
/>
<style>
  @font-face {
    font-family: 'Inter';
    src: url('/fonts/inter-var.woff2') format('woff2-variations');
    font-weight: 100 900;
    font-display: swap;
  }
</style>

Preload the font file, use font-display: swap, and self-host so you control the cache headers. Skip Google Fonts unless you have a specific reason.

Reserving space for media

.media-frame {
  aspect-ratio: 16 / 9;
  background: var(--bg-soft);
}
.media-frame > img,
.media-frame > video,
.media-frame > iframe {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

Combined with width/height attributes on images, this eliminates the most common source of layout shift.

Defer the JavaScript you don’t need on load

<!-- Critical inline -->
<script>
  // Only what must run before paint
</script>

<!-- Defer everything else -->
<script src="/app.js" defer></script>

<!-- Analytics & tag managers -->
<script src="https://plausible.io/js/script.js" defer data-domain="adfirm.net"></script>

defer is your friend. async is for scripts that don’t depend on the DOM. Inline <script> blocks should be tiny and only run code that must execute before paint.

INP — what to actually change

Most INP problems come from:

  1. Long event handlers. Break work into smaller tasks. Use requestIdleCallback for non-critical work.
  2. Hydration. Use island architecture for marketing sites. Use React Server Components or Qwik resumability for apps.
  3. Third-party scripts. Audit them. The single biggest INP improvement we’ve made for a client was removing four tag-manager scripts they weren’t actively using.

Measuring honestly

Lighthouse is a lab test. It’s useful but optimistic. Real users are slower than Lighthouse predicts. Use both:

  • Lighthouse — for catching regressions in CI. Run on every PR.
  • Chrome User Experience Report (CrUX) — for real-world data. Available via PageSpeed Insights or the CrUX API.
  • Web Vitals JS library — to capture your own users’ metrics and ship them to analytics.

If your CrUX numbers and your Lighthouse numbers diverge by a lot, trust CrUX. That’s what Google ranks on.

The 80/20 priority list

If you’ve got an hour and want to improve a site’s Core Web Vitals score, do these in order:

  1. Add fetchpriority="high" to your hero image
  2. Specify width and height on every image and video
  3. Convert hero images to AVIF or WebP
  4. Defer all non-critical scripts
  5. Audit third-party scripts and kill anything you don’t actively use
  6. Self-host fonts with font-display: swap
  7. Add aspect-ratio CSS to media containers

That sequence alone usually moves a failing site to passing.

The hidden ROI

Core Web Vitals improvements move three things you’d care about even if Google didn’t:

  • Bounce rate drops measurably below 2-second LCP
  • Conversion rate improves on the order of 1-3% per 100ms of LCP improvement
  • Crawl budget stretches further when pages load fast

Treat performance as a conversion optimization, not just an SEO optimization. The math gets a lot more interesting.

performancecore web vitalstechnical seo
Ready when you are

Let’s map out your next quarter.

Tell us what you’re trying to grow. We’ll send back a no-fluff audit and a plan within 48 hours.