Performance 22 min read UPDATED: January 12, 2026

Core Web Vitals 2026:
INP, LCP, CLS Complete Guide

Senorit
Core Web Vitals 2026: INP, LCP, CLS Masterclass

Summary

Core Web Vitals 2026 are Google's official performance metrics. INP replaced FID in March 2024, LCP measures loading time, CLS layout stability. 47% of websites fail the thresholds - this guide shows optimization with code examples and checklist.

  • 47% of websites fail Core Web Vitals (2026)
  • INP (Interaction to Next Paint) replaced FID - stricter, measures all interactions
  • Target values: INP ≤200ms, LCP ≤2.5s, CLS ≤0.1
  • Field Data (CrUX) counts for rankings, not Lab Data
  • 28-30 days until improvements are visible in Search Console

About the Author

Senorit

Verified

Web Design Agency | Founded 2025

Published: January 12, 2026
Updated: February 1, 2026

Senorit is a modern digital agency for web design, development, and SEO in the DACH region. We combine data-driven design with creative innovation to create digital experiences that convert.

Expertise in:

Core Web Vitals INP LCP CLS Performance PageSpeed
React & Next.js Astro & TypeScript UI/UX Design Core Web Vitals Optimization

47% of all websites fail Google's Core Web Vitals. Since INP (Interaction to Next Paint) replaced FID in March 2024, the requirements have become even stricter. This comprehensive guide shows you how to understand, measure, and optimize all three Core Web Vitals - with practical code examples and a complete checklist.

47%
Websites Fail
200ms
INP Threshold
2.5s
LCP Target
0.1
CLS Maximum

What are Core Web Vitals?

Core Web Vitals are Google's official metrics for user experience on a website. They measure three critical aspects of the user experience:

INP

Interaction to Next Paint - measures responsiveness to user interactions

Good: ≤ 200ms

LCP

Largest Contentful Paint - measures load time of the largest visible element

Good: ≤ 2.5s

CLS

Cumulative Layout Shift - measures unexpected layout shifts

Good: ≤ 0.1

Why are Core Web Vitals so important?

  • SEO Ranking: Core Web Vitals have been an official Google ranking factor since 2021
  • User Experience: Poor scores = higher bounce rate, fewer conversions
  • Mobile-First: Google primarily evaluates mobile performance
  • Competition: When content is equal, the faster page wins
Metric Good Needs Improvement Poor
INP ≤ 200ms 200-500ms > 500ms
LCP ≤ 2.5s 2.5-4.0s > 4.0s
CLS ≤ 0.1 0.1-0.25 > 0.25

INP: The New Metric Since March 2024

Important Change 2024

On March 12, 2024, Google introduced INP (Interaction to Next Paint) as the official Core Web Vital and replaced FID (First Input Delay). INP is significantly stricter and measures overall interactivity, not just the first input.

What does INP measure exactly?

INP measures the response time from user interaction (click, tap, keystroke) to visual feedback. Unlike FID, which only measured the first interaction, INP captures all interactions throughout the entire session.

FID (deprecated)

  • ✗ Only measured first interaction
  • ✗ Input Delay only, not Processing + Paint
  • ✗ Easy to optimize but not meaningful
  • ✗ User experience not fully represented

INP (current)

  • ✓ All interactions measured (75th percentile)
  • ✓ Complete cycle: Input + Processing + Paint
  • ✓ Real user experience represented
  • ✓ More realistic interactivity assessment

INP Optimization Techniques

1. Break up JavaScript Tasks

Long JavaScript tasks block the main thread. Split large functions into smaller chunks.

// BAD: One long task blocks the main thread
function processLargeArray(items) {
  items.forEach(item => heavyComputation(item));
}

// GOOD: Break tasks with requestIdleCallback
function processLargeArrayOptimized(items) {
  let index = 0;

  function processChunk(deadline) {
    while (index < items.length && deadline.timeRemaining() > 0) {
      heavyComputation(items[index]);
      index++;
    }

    if (index < items.length) {
      requestIdleCallback(processChunk);
    }
  }

  requestIdleCallback(processChunk);
}

2. Optimize Event Listeners

Use passive event listeners and debounce/throttle for scroll/resize events.

// BAD: Blocking scroll handler
document.addEventListener('scroll', handleScroll);

// GOOD: Passive listener + Throttling
document.addEventListener('scroll', throttle(handleScroll, 100), { passive: true });

function throttle(func, limit) {
  let inThrottle;
  return function(...args) {
    if (!inThrottle) {
      func.apply(this, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  }
}

3. Defer Third-Party Scripts

Load analytics, chat widgets, and ads only after interaction or with a delay.

// Load only when user interacts or after 5 seconds
const loadThirdParty = () => {
  if (window.thirdPartyLoaded) return;
  window.thirdPartyLoaded = true;

  const script = document.createElement('script');
  script.src = 'https://third-party.example.com/widget.js';
  document.head.appendChild(script);
};

// On first interaction
['mousedown', 'touchstart', 'keydown'].forEach(event => {
  document.addEventListener(event, loadThirdParty, { once: true });
});

// Or after timeout
setTimeout(loadThirdParty, 5000);

LCP: Optimizing Largest Contentful Paint

LCP measures how long it takes for the largest visible element in the viewport to render. This is typically a hero image, a large heading, or a video poster.

LCP-relevant elements:

  • • <img> images
  • • <image> inside SVG
  • • <video> poster images
  • • Elements with background-image
  • • Block-level text elements (h1, p, etc.)
  • • <svg> graphics

The 5 Most Important LCP Optimizations

1. Preload the LCP Element

Load the LCP element as early as possible with a preload hint.

<!-- In <head> - BEFORE other resources -->
<link
  rel="preload"
  as="image"
  href="/hero-image.webp"
  fetchpriority="high"
/>

<!-- For responsive images -->
<link
  rel="preload"
  as="image"
  href="/hero-mobile.webp"
  media="(max-width: 768px)"
  fetchpriority="high"
/>

2. Use Optimized Image Formats

Use modern image formats and responsive images with srcset.

<picture>
  <!-- AVIF: Best compression, limited support -->
  <source
    type="image/avif"
    srcset="
      /hero-400.avif 400w,
      /hero-800.avif 800w,
      /hero-1200.avif 1200w
    "
    sizes="(max-width: 768px) 100vw, 50vw"
  />
  <!-- WebP: Good compression, broad support -->
  <source
    type="image/webp"
    srcset="
      /hero-400.webp 400w,
      /hero-800.webp 800w,
      /hero-1200.webp 1200w
    "
    sizes="(max-width: 768px) 100vw, 50vw"
  />
  <!-- Fallback for older browsers -->
  <img
    src="/hero-800.jpg"
    alt="Hero"
    width="1200"
    height="600"
    loading="eager"
    fetchpriority="high"
    decoding="async"
  />
</picture>

3. Optimize Server Response Time (TTFB)

Time to First Byte directly impacts LCP. Optimize your backend and hosting.

  • Use a CDN: Cloudflare, Vercel Edge, AWS CloudFront
  • Server-Side Caching: Redis, Varnish, or Edge Caching
  • Optimize Database: Indexes, query optimization
  • HTTP/2 or HTTP/3: Multiplexing for parallel requests
  • Static Generation: Astro, Next.js SSG instead of SSR

4. Eliminate Render-Blocking Resources

CSS and JavaScript can block rendering. Optimize the loading order.

<!-- Critical CSS inline -->
<style>
  /* Only necessary CSS for Above-the-Fold */
  .hero { ... }
  .nav { ... }
</style>

<!-- Load rest of CSS asynchronously -->
<link
  rel="preload"
  href="/styles.css"
  as="style"
  onload="this.onload=null;this.rel='stylesheet'"
/>
<noscript><link rel="stylesheet" href="/styles.css"></noscript>

<!-- JavaScript defer or async -->
<script src="/app.js" defer></script>

5. Optimize Fonts

Web fonts can delay LCP when text is the LCP element.

<!-- Font preload with font-display: swap -->
<link
  rel="preload"
  href="/fonts/inter.woff2"
  as="font"
  type="font/woff2"
  crossorigin
/>

<style>
  @font-face {
    font-family: 'Inter';
    src: url('/fonts/inter.woff2') format('woff2');
    font-display: swap; /* Shows fallback font until loaded */
    font-weight: 400;
  }
</style>

CLS: Understanding Cumulative Layout Shift

CLS measures how much elements on the page unexpectedly shift. A high CLS value means a frustrating user experience - everyone knows it: you want to click a button, and suddenly everything jumps.

The most common CLS causes:

  • ✗ Images without width/height attributes
  • ✗ Ads, embeds, iFrames without reserved space
  • ✗ Dynamically inserted content
  • ✗ Web fonts causing FOIT/FOUT
  • ✗ Animations without transform
  • ✗ Cookie banners that shift layout
  • ✗ Lazy-loaded content without placeholder
  • ✗ Sticky headers that shift other elements

CLS Optimization Best Practices

1. Always specify dimensions for images/videos

<!-- BAD: No dimensions -->
<img src="photo.jpg" alt="Photo">

<!-- GOOD: Explicit width/height -->
<img src="photo.jpg" alt="Photo" width="800" height="600">

<!-- ALSO GOOD: CSS aspect-ratio -->
<style>
  .responsive-image {
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
    object-fit: cover;
  }
</style>
<img src="photo.jpg" alt="Photo" class="responsive-image">

2. Placeholders for dynamic content

<!-- Reserved space for ads -->
<div class="ad-container" style="min-height: 250px;">
  <!-- Ad loads here -->
</div>

<!-- Skeleton for lazy-loaded content -->
<div class="card-skeleton" style="height: 300px;">
  <div class="skeleton-image" style="height: 180px;"></div>
  <div class="skeleton-text"></div>
</div>

/* CSS for Skeleton */
.skeleton-image {
  background: linear-gradient(90deg, #1a1a1a 25%, #2a2a2a 50%, #1a1a1a 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
}

@keyframes shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

3. Use transform instead of position for animations

/* BAD: Shifts the layout */
.modal-open {
  top: 100px; /* Causes Layout Shift */
  left: 50px;
}

/* GOOD: Uses GPU, no Layout Shift */
.modal-open {
  transform: translateY(100px) translateX(50px);
}

/* BAD: Changes dimensions */
.expanded {
  width: 200px;
  height: 300px;
}

/* GOOD: Scales without layout change */
.expanded {
  transform: scale(1.2);
}

4. Implement cookie banners correctly

/* BAD: Banner shifts content */
.cookie-banner {
  position: relative; /* Takes space in flow */
}

/* GOOD: Fixed or Sticky, no shift */
.cookie-banner {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 9999;
}

/* ALSO GOOD: Overlay instead of banner */
.cookie-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.8);
}

Measurement and Monitoring

There are two types of Core Web Vitals data: Field Data (real user data) and Lab Data (simulated tests). For Google rankings, only Field Data counts!

Field Data (RUM)

Real user data from Chrome User Experience Report (CrUX).

  • Google Search Console: Core Web Vitals Report
  • PageSpeed Insights: "Field Data" section
  • CrUX Dashboard: Historical data
  • web-vitals.js: Custom RUM implementation

Lab Data (Synthetic)

Simulated tests in controlled environments.

  • Lighthouse: Integrated in Chrome DevTools
  • PageSpeed Insights: "Lab Data" section
  • WebPageTest: Detailed waterfall analysis
  • Debugbear: Continuous monitoring

Implementing web-vitals.js

// Installation: npm install web-vitals

import { onINP, onLCP, onCLS } from 'web-vitals';

// Send to analytics
function sendToAnalytics(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    delta: metric.delta,
    id: metric.id,
    rating: metric.rating, // 'good', 'needs-improvement', or 'poor'
  });

  // Beacon API for reliable transmission
  if (navigator.sendBeacon) {
    navigator.sendBeacon('/analytics', body);
  } else {
    fetch('/analytics', { body, method: 'POST', keepalive: true });
  }
}

// Measure all Core Web Vitals
onINP(sendToAnalytics);
onLCP(sendToAnalytics);
onCLS(sendToAnalytics);

Pro Tip: Attribution for Debugging

Use the attribution builds for detailed debug information:

import { onINP } from 'web-vitals/attribution';

onINP((metric) => {

});

Advanced Optimization Strategies

1

Priority Hints for Resources

With fetchpriority you can tell the browser which resources are important.

<!-- Hero image with high priority -->
<img src="hero.jpg" fetchpriority="high" alt="Hero">

<!-- Below-the-fold images with low priority -->
<img src="footer-logo.png" fetchpriority="low" loading="lazy" alt="Logo">

<!-- Scripts -->
&lt;script src="critical.js" fetchpriority="high"&gt;&lt;/script&gt;
&lt;script src="analytics.js" fetchpriority="low" defer&gt;&lt;/script&gt;
2

Speculation Rules API (Chrome 2026)

Prerendering for nearly instant navigation.

<script type="speculationrules">
{
  "prerender": [{
    "source": "list",
    "urls": ["/about", "/contact", "/pricing"]
  }],
  "prefetch": [{
    "source": "document",
    "where": {
      "href_matches": "/*"
    },
    "eagerness": "moderate"
  }]
}
</script>
3

View Transitions API

Smooth transitions between pages without Layout Shifts.

// In Astro 4.0+
---
// In astro.config.mjs
export default defineConfig({
  experimental: {
    viewTransitions: true
  }
});
---

<!-- In your Layout -->
<head>
  <ViewTransitions />
</head>

<!-- Animated elements -->
<h1 transition:name="page-title">Page Title</h1>
<img transition:name="hero" src="/hero.jpg" />
4

Scheduler API for JavaScript

Prioritize JavaScript tasks for better INP values.

// High priority: User-visible updates
scheduler.postTask(() => {
  updateUI();
}, { priority: 'user-blocking' });

// Low priority: Analytics, logging
scheduler.postTask(() => {
  sendAnalytics();
}, { priority: 'background' });

// Yielding to Main Thread
async function processItems(items) {
  for (const item of items) {
    processItem(item);
    await scheduler.yield(); // Release main thread
  }
}

Complete Code Example: Optimized Hero Section

Here is a fully optimized hero section example that implements all Core Web Vitals best practices:

<!-- HTML: Optimized Hero Section -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <!-- Preconnect for external resources -->
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://cdn.example.com" crossorigin>

  <!-- Preload critical resources -->
  <link
    rel="preload"
    as="image"
    href="/hero-mobile.webp"
    media="(max-width: 768px)"
    fetchpriority="high"
  >
  <link
    rel="preload"
    as="image"
    href="/hero-desktop.webp"
    media="(min-width: 769px)"
    fetchpriority="high"
  >
  <link rel="preload" as="font" href="/fonts/inter.woff2" type="font/woff2" crossorigin>

  <!-- Critical CSS inline -->
  <style>
    /* Reset and Critical Styles */
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    @font-face {
      font-family: 'Inter';
      src: url('/fonts/inter.woff2') format('woff2');
      font-display: swap;
    }

    body {
      font-family: 'Inter', system-ui, sans-serif;
      background: #0a0a0a;
      color: #fff;
    }

    /* Hero with fixed dimensions for CLS */
    .hero {
      min-height: 100vh;
      display: flex;
      align-items: center;
      position: relative;
      overflow: hidden;
    }

    .hero-image {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      object-fit: cover;
    }

    .hero-content {
      position: relative;
      z-index: 1;
      max-width: 1200px;
      margin: 0 auto;
      padding: 2rem;
    }

    /* Animations with transform (no Layout Shift) */
    .hero-title {
      opacity: 0;
      transform: translateY(20px);
      animation: fadeInUp 0.6s ease-out 0.1s forwards;
    }

    @keyframes fadeInUp {
      to { opacity: 1; transform: translateY(0); }
    }

    /* Button with fixed size */
    .cta-button {
      display: inline-block;
      padding: 1rem 2rem;
      min-width: 200px;
      text-align: center;
      /* ... more styles */
    }
  </style>
</head>
<body>
  <section class="hero">
    <!-- Hero image with all optimizations -->
    <picture>
      <source
        type="image/webp"
        media="(max-width: 768px)"
        srcset="/hero-mobile.webp"
      >
      <source
        type="image/webp"
        media="(min-width: 769px)"
        srcset="/hero-desktop.webp"
      >
      <img
        src="/hero-desktop.jpg"
        alt="Hero Image"
        class="hero-image"
        width="1920"
        height="1080"
        fetchpriority="high"
        decoding="async"
      >
    </picture>

    <div class="hero-content">
      <h1 class="hero-title">Your Headline Here</h1>
      <a href="/contact" class="cta-button">Get Started</a>
    </div>
  </section>

  <!-- Non-critical CSS async -->
  <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'">

  <!-- JavaScript defer -->
  <script src="/app.js" defer></script>
</body>
</html>

Core Web Vitals Checklist 2026

INP Optimization

LCP Optimization

CLS Optimization

Frequently Asked Questions

How long until improvements are visible in Search Console?

Google uses a 28-day rolling average of CrUX data. After an improvement, it typically takes 28-30 days for the new values to be fully reflected in Search Console. For larger websites, it may be slightly faster as more user data is collected.

My Lab data is good, but Field data is bad - why?

Lab tests are conducted under controlled conditions, while Field data represents real user experiences. Possible reasons: Slower devices of your users, poor internet connections (3G/4G), Third-Party Scripts that aren't loaded in lab, or geographic differences (server far from users).

Does the 75th percentile apply to all metrics?

Yes, Google uses the 75th percentile for all Core Web Vitals. This means: 75% of your users must reach the "Good" thresholds for the page to pass. This ensures the majority of users have a good experience, not just those with fast devices.

How do poor Core Web Vitals affect rankings?

Core Web Vitals are a tiebreaker signal: when content is equivalent, the faster page wins. Content remains more important than performance, but poor CWV can disadvantage you against competitors with similar content. In competitive niches, the difference can be significant.

Do I need good CWV for every URL or just the domain?

Google evaluates at the URL level, but uses aggregated data from similar pages when there isn't enough URL-specific data available. With low traffic, the Origin-level (entire domain) evaluation is used. Ideally, you should optimize all important pages individually.

Conclusion: Core Web Vitals as Competitive Advantage

Core Web Vitals are more than just an SEO metric - they're an indicator of user satisfaction. With the right optimizations you can:

  • Achieve better Google rankings through technical excellence
  • Reduce bounce rates by 20-30%
  • Increase conversion rates by 10-25%
  • Better serve mobile users (70%+ of traffic)
  • Stand out from 47% of competitors who fail

Start by measuring your current values, prioritize the biggest problems, and work systematically through our checklist. With this guide, you have all the tools for top performance in 2026.

Want Core Web Vitals Optimization?

We analyze your website and optimize all Core Web Vitals to "Good". With a guarantee for better scores.