Core Web Vitals 12 min read

Core Web Vitals Optimization Guide 2025 | LCP, INP & CLS

Complete Core Web Vitals optimization guide for 2025. Learn how to improve LCP, INP, and CLS to pass Google's thresholds, boost rankings, and improve UX.

By Rankture Team
Core Web Vitals Optimization Guide 2025 | LCP, INP & CLS

Core Web Vitals are Google’s official user experience metrics and a confirmed ranking factor since 2021. In 2025, these metrics are more important than ever, with Google using them to determine page experience scores that directly impact search rankings.

If your site has poor Core Web Vitals, you’re losing rankings to competitors—even if your content is better. This guide shows you exactly how to optimize each Core Web Vital metric to pass Google’s thresholds and improve both SEO and user experience.

Quick Wins (Start Here)

What Are Core Web Vitals?

Core Web Vitals are three specific metrics that measure real-world user experience:

  1. LCP (Largest Contentful Paint) - Loading performance
  2. INP (Interaction to Next Paint) - Interactivity (replaced FID in 2024)
  3. CLS (Cumulative Layout Shift) - Visual stability

Together, these metrics capture the most frustrating aspects of poor web performance: slow loading, unresponsive interactions, and unexpected layout shifts.

Why Core Web Vitals Matter for SEO

Google’s official statement: “Page experience signals in ranking will include Core Web Vitals in addition to our existing signals for mobile-friendliness, safe-browsing, HTTPS-security, and intrusive interstitial guidelines.”

What this means:

Real-world impact:


Core Web Vitals Thresholds (2025)

Google defines three scoring zones for each metric:

MetricGoodNeeds ImprovementPoor
LCP≤ 2.5s2.5s - 4.0s> 4.0s
INP≤ 200ms200ms - 500ms> 500ms
CLS≤ 0.10.1 - 0.25> 0.25

Goal: Get 75% of your page views in the “Good” range for all three metrics.

Where to check your scores:


1. Largest Contentful Paint (LCP) Optimization

What LCP Measures: The render time of the largest image or text block visible in the viewport.

Why it matters: LCP represents perceived loading performance—when does the user see the main content?

Target: ≤ 2.5 seconds

Common LCP Elements

Your LCP element is usually:

How to identify your LCP element:

  1. Open Chrome DevTools → Performance tab
  2. Record page load
  3. Look for “LCP” marker in timeline
  4. Inspect which element triggered it

How to Optimize LCP

1. Optimize Images

Problem: Large, unoptimized images are the #1 cause of slow LCP.

Solutions:

a) Use modern image formats:

<picture>
  <source srcset="hero.avif" type="image/avif">
  <source srcset="hero.webp" type="image/webp">
  <img src="hero.jpg" alt="Hero image" width="1200" height="600">
</picture>

b) Compress images:

c) Resize images:

d) Set explicit dimensions:

<img src="hero.jpg" alt="Hero" width="1200" height="600" loading="eager">

e) Use loading=“eager” for above-fold images:

<img src="hero.jpg" alt="Hero" loading="eager">

Don’t use loading="lazy" on LCP images—it delays loading!

2. Preload Critical Resources

Tell the browser to fetch important resources immediately:

<link rel="preload" as="image" href="/hero.webp" type="image/webp">
<link rel="preload" as="font" href="/fonts/inter.woff2" type="font/woff2" crossorigin>

What to preload:

Don’t over-preload: Only preload 2-3 truly critical resources.

3. Reduce Server Response Time (TTFB)

TTFB (Time to First Byte) = how long until the server starts sending data

Target: < 600ms

How to improve TTFB:

4. Eliminate Render-Blocking Resources

Render-blocking resources = CSS/JS files that prevent the page from displaying until they’re loaded.

How to fix:

a) Inline critical CSS:

<style>
  /* Inline only above-fold CSS here */
  .hero { background: blue; padding: 100px; }
</style>
<link rel="preload" href="/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">

b) Defer non-critical JavaScript:

<script src="/analytics.js" defer></script>

c) Use async for independent scripts:

<script src="/chat-widget.js" async></script>

Difference:

5. Use Server-Side Rendering (SSR)

For React/Vue/Angular apps, use SSR or static site generation to serve pre-rendered HTML.

Frameworks with built-in SSR:

Why it helps: User sees content immediately, before JavaScript loads.


2. Interaction to Next Paint (INP) Optimization

What INP Measures: The time from user interaction (click, tap, keyboard) to the next visual update.

Why it matters: INP captures overall responsiveness—does the site feel snappy or sluggish?

Target: ≤ 200ms

Note: INP replaced FID (First Input Delay) in 2024. FID only measured the delay before processing starts; INP measures the full interaction latency.

Common INP Issues

How to Optimize INP

1. Break Up Long Tasks

Problem: JavaScript tasks > 50ms block the main thread and delay interactions.

Solution: Break long tasks into smaller chunks using setTimeout or requestIdleCallback:

// Bad: Long blocking task
function processLargeArray(items) {
  for (let i = 0; i < items.length; i++) {
    // Complex processing
  }
}

// Good: Chunked processing
async function processLargeArray(items, chunkSize = 100) {
  for (let i = 0; i < items.length; i += chunkSize) {
    const chunk = items.slice(i, i + chunkSize);
    chunk.forEach(item => {
      // Process item
    });

    // Yield to browser
    await new Promise(resolve => setTimeout(resolve, 0));
  }
}

2. Optimize Event Handlers

Problem: Heavy event handlers (click, scroll, input) delay interactions.

Solutions:

a) Debounce/throttle expensive operations:

// Debounce search input
let timeout;
searchInput.addEventListener('input', (e) => {
  clearTimeout(timeout);
  timeout = setTimeout(() => {
    performSearch(e.target.value);
  }, 300);
});

b) Use passive event listeners:

// Tells browser it's safe to scroll while handler runs
element.addEventListener('touchstart', handler, { passive: true });

c) Avoid layout thrashing:

// Bad: Read-write-read-write (causes multiple reflows)
elements.forEach(el => {
  const height = el.offsetHeight; // Read
  el.style.height = height + 10 + 'px'; // Write
});

// Good: Read all, then write all
const heights = elements.map(el => el.offsetHeight); // Read batch
elements.forEach((el, i) => {
  el.style.height = heights[i] + 10 + 'px'; // Write batch
});

3. Code Splitting

Problem: Loading massive JavaScript bundles delays everything.

Solution: Split code so users only load what they need:

Webpack example:

// Instead of importing everything
import { moduleA, moduleB, moduleC } from './utils';

// Dynamically import when needed
button.addEventListener('click', async () => {
  const { moduleA } = await import('./utils');
  moduleA.doSomething();
});

Next.js example:

import dynamic from 'next/dynamic';

const DynamicComponent = dynamic(() => import('../components/Heavy'), {
  loading: () => <p>Loading...</p>,
});

4. Web Workers for Heavy Computation

Move CPU-intensive work off the main thread:

// main.js
const worker = new Worker('worker.js');
worker.postMessage({ data: largeDataset });
worker.onmessage = (e) => {
  console.log('Result:', e.data);
};

// worker.js
self.onmessage = (e) => {
  const result = processData(e.data);
  self.postMessage(result);
};

Use web workers for:

5. Minimize Third-Party Impact

Problem: Third-party scripts (ads, analytics, chat widgets) can destroy INP.

Solutions:

a) Load third-parties last:

<script src="/your-app.js"></script>
<!-- Load third-parties after your code -->
<script src="https://cdn.thirdparty.com/widget.js" defer></script>

b) Use facade patterns: Instead of loading YouTube/Vimeo immediately, show a poster image and only load the iframe when clicked.

c) Self-host analytics: Instead of Google Analytics, use lightweight self-hosted solutions like Plausible or Fathom.


3. Cumulative Layout Shift (CLS) Optimization

What CLS Measures: Unexpected layout shifts—how much visible content moves around during page load.

Why it matters: Nothing is more frustrating than clicking a button, only for an ad to load and make you click the wrong thing.

Target: ≤ 0.1

Common CLS Causes

  1. Images without dimensions
  2. Ads, embeds, iframes without reserved space
  3. Web fonts causing text reflow (FOIT/FOUT)
  4. Dynamically injected content
  5. Animations that trigger layout

How to Optimize CLS

1. Set Explicit Dimensions on Images/Videos

Bad (causes CLS):

<img src="product.jpg" alt="Product">

Good (reserves space):

<img src="product.jpg" alt="Product" width="800" height="600">

Responsive images:

<img src="product.jpg" alt="Product"
     width="800" height="600"
     style="width: 100%; height: auto;">

Modern browsers use width and height to calculate aspect ratio, even with responsive CSS.

2. Reserve Space for Ads and Embeds

Bad:

<div id="ad-slot"></div>
<script>loadAd('#ad-slot')</script>

Good:

<div id="ad-slot" style="min-height: 250px;">
  <!-- Ad loads here -->
</div>

Better (aspect ratio box):

<div style="aspect-ratio: 16/9; background: #f0f0f0;">
  <iframe src="youtube-video"></iframe>
</div>

3. Optimize Web Font Loading

Problem: Web fonts cause FOUT (Flash of Unstyled Text) or FOIT (Flash of Invisible Text), both causing CLS.

Solutions:

a) Use font-display: swap:

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

b) Preload critical fonts:

<link rel="preload" as="font" href="/fonts/inter.woff2"
      type="font/woff2" crossorigin>

c) Match fallback font metrics: Use a fallback font with similar dimensions to reduce CLS when fonts swap.

d) Consider system fonts:

font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
             Roboto, "Helvetica Neue", Arial, sans-serif;

Zero CLS, zero load time.

4. Avoid Inserting Content Above Existing Content

Bad:

// Loads content, pushes everything down
banner.insertAdjacentHTML('afterend', newContent);

Good:

// Reserve space first
const placeholder = document.createElement('div');
placeholder.style.minHeight = '200px';
banner.after(placeholder);

// Load content into reserved space
fetchContent().then(content => {
  placeholder.innerHTML = content;
});

5. Use CSS Transforms for Animations

Bad (causes layout shift):

.box:hover {
  width: 200px; /* Triggers layout */
}

Good (no layout shift):

.box:hover {
  transform: scale(1.1); /* GPU-accelerated */
}

GPU-accelerated properties (no layout shift):

Layout-triggering properties (causes CLS):


Tools to Measure Core Web Vitals

1. Google PageSpeed Insights

https://pagespeed.web.dev/

Pros:

Cons:

Because it tests one URL at a time, PageSpeed Insights is poor at spotting template-level problems. The Core Web Vitals checker runs across your pages at once and groups failures by metric, so a bad template shows up as a cluster rather than a URL you happened to pick.

2. Google Search Console

Dashboard → Experience → Core Web Vitals

Pros:

Cons:

If this report is where you found out your Core Web Vitals assessment failed, start there — that 28-day window changes how you should read the verdict, and it explains most cases of passing Lighthouse while failing in Search Console.

3. Chrome DevTools (Lighthouse)

Chrome → DevTools → Lighthouse tab

Pros:

Cons:

4. Rankture SEO Audit

Try free audit

Pros:

Cons:

5. WebPageTest

https://www.webpagetest.org/

Pros:

Cons:


Core Web Vitals Optimization Checklist

LCP Checklist

INP Checklist

CLS Checklist


Common Core Web Vitals Issues & Fixes

Issue: “LCP element was a background image”

Problem: CSS background images can’t be preloaded easily.

Fix: Use <img> tags instead of CSS backgrounds for critical images:

<!-- Instead of: -->
<div class="hero" style="background-image: url('/hero.jpg')"></div>

<!-- Use: -->
<div class="hero">
  <img src="/hero.jpg" alt="Hero" loading="eager">
</div>

Issue: “INP poor due to third-party scripts”

Problem: Ads, analytics, and chat widgets slow interactions.

Fix: Load third-parties after user interaction:

let analyticsLoaded = false;

document.addEventListener('scroll', () => {
  if (!analyticsLoaded) {
    const script = document.createElement('script');
    script.src = 'https://analytics.com/tracker.js';
    document.body.appendChild(script);
    analyticsLoaded = true;
  }
}, { once: true });

Issue: “CLS from web fonts”

Problem: Text shifts when web fonts load.

Fix: Use font-display: optional for non-critical fonts:

@font-face {
  font-family: 'DecorativeFont';
  src: url('/fonts/decorative.woff2') format('woff2');
  font-display: optional; /* Only use if available immediately */
}

Issue: “Mobile CWV good, desktop poor” (or vice versa)

Problem: Different content/scripts for mobile vs desktop.

Fix: Use responsive design with same HTML for both. Optimize images with srcset:

<img srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
     sizes="(max-width: 600px) 480px, (max-width: 1000px) 800px, 1200px"
     src="large.jpg" alt="Responsive image">

Conclusion

Core Web Vitals are now a mandatory part of SEO. Sites with excellent CWV scores have a ranking advantage over slower competitors, especially in competitive niches.

Focus areas by priority:

  1. LCP - Biggest impact on rankings, most noticeable to users
  2. CLS - Second biggest impact, most frustrating to users
  3. INP - Important but harder to measure/optimize

Quick wins:

Long-term optimizations:


Get a Free Core Web Vitals Audit

Want to know your exact CWV scores and get AI-powered recommendations?

Run a free SEO audit with Rankture and get:

No signup required for your first audit. Get results in 60 seconds.


Tags:

core web vitals lcp optimization inp optimization cls optimization page speed google ranking factors performance optimization

Share this article:

Ready to improve your SEO?

Get a free SEO audit and see exactly what needs fixing on your site

Start Free Audit