Back to Blog
Guide7 min read2026-05-01

Core Web Vitals: A Developer's Guide to Google's Performance Metrics

Understand Google's Core Web Vitals metrics — LCP, INP, and CLS — and learn exactly how to improve each one for better rankings and user experience.

Core Web Vitals: A Developer's Guide to Google's Performance Metrics

Core Web Vitals are a set of real-world performance metrics that Google uses to measure user experience. They are a confirmed Google ranking factor, meaning poor scores directly affect your search visibility. More importantly, they measure things users actually feel: how quickly the page loads, how responsive it is to interaction, and how stable the layout is. This guide explains each metric and gives you concrete steps to improve your scores.

The Three Core Web Vitals

LCP — Largest Contentful Paint

What it measures: How long it takes for the largest visible element (usually a hero image or headline) to render in the viewport.

Good: ≤ 2.5 seconds | Needs Improvement: 2.5–4.0s | Poor: > 4.0s

LCP is primarily affected by:

  • Server response time (TTFB)
  • Render-blocking resources (CSS, fonts, JavaScript)
  • Image loading time

How to improve LCP:

Preload your LCP image — tell the browser to fetch it immediately, before it parses HTML far enough to discover it:

<link rel="preload" as="image" href="/hero.webp" fetchpriority="high" />

Eliminate render-blocking CSS by inlining critical styles:

<style>
  /* Only the CSS needed for above-the-fold content */
  .hero { background: #000; color: #fff; min-height: 500px; }
</style>
<link rel="preload" href="/styles.css" as="style" onload="this.rel='stylesheet'" />

Optimize images: use WebP or AVIF, serve at display size, and enable CDN caching with aggressive TTLs.

Reduce TTFB: use a CDN, cache server responses, and optimize your slowest API queries.

INP — Interaction to Next Paint

What it measures: The latency between user input (click, tap, key press) and the next frame painted to screen. Measures responsiveness across all interactions during the page visit.

Good: ≤ 200ms | Needs Improvement: 200–500ms | Poor: > 500ms

INP replaced FID (First Input Delay) in March 2024. Unlike FID, INP measures all interactions, not just the first one.

How to improve INP:

Break up long tasks — the browser cannot paint between JavaScript tasks, so long synchronous operations directly increase INP:

// Long synchronous task blocking the main thread
function processLargeDataset(items) {
  items.forEach(item => expensiveOperation(item)); // blocks for 300ms
}

// Chunked with yielding — browser can paint between chunks
async function processLargeDatasetYielding(items) {
  for (let i = 0; i < items.length; i++) {
    expensiveOperation(items[i]);
    if (i % 50 === 0) {
      await scheduler.yield(); // yield to browser between chunks
    }
  }
}

Defer non-critical JavaScript: anything that is not needed for the first interaction should be loaded asynchronously.

<!-- Defer analytics and non-critical scripts -->
<script src="/analytics.js" defer></script>
<script src="/chat-widget.js" async></script>

CLS — Cumulative Layout Shift

What it measures: The total amount of unexpected visual movement during the page's lifetime. When elements shift position as the page loads, users click the wrong thing and lose their place.

Good: ≤ 0.1 | Needs Improvement: 0.1–0.25 | Poor: > 0.25

The most common causes of CLS:

Images without dimensions — the browser does not know how much space to reserve:

<!-- Bad: no dimensions, causes layout shift when image loads -->
<img src="/product.jpg" alt="Product" />

<!-- Good: explicit dimensions prevent layout shift -->
<img src="/product.jpg" alt="Product" width="800" height="600" />

Late-loading web fonts — text renders in the fallback font, then shifts to the web font (FOUT):

/* Use font-display: optional to prevent layout shift
   (browser uses fallback if font is not cached) */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter.woff2') format('woff2');
  font-display: optional;
}

Dynamically injected content — ads, banners, or cookie notices inserted above existing content push everything down. Always reserve space for dynamic content:

.ad-container {
  min-height: 250px; /* Reserve space before ad loads */
}

Measuring Your Core Web Vitals

Field data (real users): Google Search Console → Core Web Vitals report. Shows actual user experience, 28-day aggregation.

Lab data (simulated): Lighthouse in Chrome DevTools (Cmd+Shift+P → "Lighthouse"). Good for development iteration, does not replace field data.

JavaScript measurement:

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

onLCP(({ value }) => console.log('LCP:', value));
onINP(({ value }) => console.log('INP:', value));
onCLS(({ value }) => console.log('CLS:', value));

Deploying for Better Core Web Vitals

Where and how you host your frontend significantly impacts LCP through TTFB and CDN edge caching. [PandaStack](https://dashboard.pandastack.io) supports static site deployments — deploy your built React or static site to get optimized content delivery for your static assets.

Action Priority

  1. 1Fix CLS first — it is usually the fastest win (add image dimensions, reserve space for dynamic content)
  2. 2Improve LCP by preloading hero images and reducing TTFB
  3. 3Audit INP last — requires JavaScript profiling with DevTools

Track your scores in Google Search Console weekly. Improvements in Core Web Vitals take 28+ days to fully appear in field data, so be patient after making changes.

Ready to deploy?

Start free on PandaStack — no credit card required.

Start free on PandaStack

More in Guide

Browse all Guide articles →

See also