Back to blog
Engineering #performance#Core Web Vitals#LCP

Web Performance Checklist 2026: Core Web Vitals and Beyond

Page speed affects SEO, conversion, and user experience. A practical checklist covering LCP, CLS, INP, and the optimizations that actually move metrics.

15 min · January 14, 2026 · Updated January 27, 2026
Topic relevant background image

TL;DR

  • Core Web Vitals: LCP ≤2.5s, CLS ≤0.1, INP ≤200ms. These affect SEO ranking and user experience.
  • Field data (real users) matters more than lab data (Lighthouse)—Google ranks based on the 75th percentile of actual visits.
  • LCP: Optimize images (WebP/AVIF, proper sizing), preload above-fold images, reduce server response time to <800ms.
  • CLS: Set explicit dimensions on images and ads, use font-display: swap with size-adjusted fallbacks.
  • INP: Break long tasks (yield every 8–16ms), defer non-critical JavaScript, offload heavy work to Web Workers.
  • Add Lighthouse CI budgets to deployment pipelines to prevent performance regression.
  • Performance is an ongoing practice, not a one-time fix.

Core Web Vitals Overview

Google’s Core Web Vitals are the metrics that matter for SEO and user experience:

MetricFull NameMeasuresGoodNeeds ImprovementPoor
LCPLargest Contentful PaintLoad speed≤2.5s2.5–4.0s>4.0s
CLSCumulative Layout ShiftVisual stability≤0.10.1–0.25>0.25
INPInteraction to Next PaintInteractivity≤200ms200–500ms>500ms

These are measured at the 75th percentile of real user experiences. If 75% of your users have LCP under 2.5s, you pass.

Lab vs. Field Data

Data TypeSourceUse Case
Lab dataLighthouse, WebPageTestDevelopment, debugging
Field dataChrome UX Report (CrUX), RUMActual ranking signal

Important: Lighthouse scores don’t directly affect rankings. Field data does. A site can score 100 in Lighthouse but fail Core Web Vitals if real users experience poor performance.

Use lab data to diagnose issues. Use field data to measure success.

LCP Optimization

Largest Contentful Paint measures when the main content becomes visible.

What Affects LCP

FactorImpactPriority
Server response timeHighFix first
Render-blocking resourcesHighFix early
Resource load timeHighOptimize images
Client-side renderingMediumConsider SSR

Server Response Time (<800ms)

# Enable compression
gzip on;
gzip_types text/plain text/css application/json application/javascript;

# Caching headers
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

Optimize database queries, use CDN, upgrade hosting if TTFB exceeds 800ms.

Image Optimization

Images are typically the LCP element. Optimize them:

<!-- Responsive images with modern formats -->
<picture>
  <source 
    srcset="image.avif" 
    type="image/avif"
  >
  <source 
    srcset="image.webp" 
    type="image/webp"
  >
  <img 
    src="image.jpg" 
    alt="Description"
    width="1200" 
    height="630"
    loading="eager"
    fetchpriority="high"
  >
</picture>

For LCP images specifically:

  • Use loading="eager" (not lazy)
  • Add fetchpriority="high"
  • Preload in <head>
<link 
  rel="preload" 
  as="image" 
  href="hero.webp"
  fetchpriority="high"
>

Eliminate Render-Blocking Resources

<!-- Defer non-critical JavaScript -->
<script src="analytics.js" defer></script>

<!-- Async load non-critical CSS -->
<link 
  rel="preload" 
  href="below-fold.css" 
  as="style" 
  onload="this.onload=null;this.rel='stylesheet'"
>

Inline critical CSS for above-fold content:

<style>
  /* Critical CSS inlined */
  .hero { ... }
  .nav { ... }
</style>

CLS Optimization

Cumulative Layout Shift measures unexpected layout movement.

What Causes Layout Shifts

CauseFix
Images without dimensionsSet width/height
Ads/embeds without spaceReserve placeholder
Web fonts causing FOIT/FOUTfont-display: swap + fallback
Dynamic content insertionReserve space or animate

Always Set Image Dimensions

<!-- Always include width and height -->
<img 
  src="image.jpg" 
  alt="Description"
  width="800" 
  height="600"
>

Or use CSS aspect ratio:

.image-container {
  aspect-ratio: 16 / 9;
}

.image-container img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

Web Font Optimization

/* Size-adjusted fallback prevents layout shift */
@font-face {
  font-family: 'Custom Font';
  src: url('font.woff2') format('woff2');
  font-display: swap;
  size-adjust: 105%;
  ascent-override: 95%;
  descent-override: 22%;
}

/* Fallback stack with similar metrics */
body {
  font-family: 'Custom Font', 'Helvetica Neue', sans-serif;
}

Reserve Space for Dynamic Content

/* Reserve ad space */
.ad-slot {
  min-height: 250px;
  background: #f0f0f0;
}

/* Skeleton for async content */
.content-skeleton {
  min-height: 200px;
  animation: shimmer 1.5s infinite;
}

INP Optimization

Interaction to Next Paint measures responsiveness to user input.

What Causes Poor INP

CauseFix
Long JavaScript tasksBreak into smaller chunks
Heavy event handlersDebounce/throttle
Main thread blockingUse Web Workers
Large DOMVirtualize lists

Break Long Tasks

// BAD: Long blocking task
function processAllItems(items) {
  items.forEach(item => heavyOperation(item));
}

// GOOD: Yield to main thread
async function processAllItems(items) {
  for (const item of items) {
    heavyOperation(item);
    
    // Yield every 8-16ms
    if (shouldYield()) {
      await scheduler.yield();
    }
  }
}

function shouldYield() {
  // Use scheduler.yield() or requestIdleCallback
  return performance.now() % 16 < 1;
}

Use requestIdleCallback

// Process work during idle time
function processInIdle(items, callback) {
  const queue = [...items];
  
  function processChunk(deadline) {
    while (queue.length > 0 && deadline.timeRemaining() > 5) {
      const item = queue.shift();
      processItem(item);
    }
    
    if (queue.length > 0) {
      requestIdleCallback(processChunk);
    } else {
      callback();
    }
  }
  
  requestIdleCallback(processChunk);
}

Offload to Web Workers

// Main thread
const worker = new Worker('worker.js');

worker.postMessage({ data: heavyData });

worker.onmessage = (event) => {
  // Result from worker
  updateUI(event.data);
};

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

Performance Budget

Setting Budgets

// lighthouserc.json
{
  "ci": {
    "assert": {
      "assertions": {
        "categories:performance": ["error", { "minScore": 0.9 }],
        "first-contentful-paint": ["warn", { "maxNumericValue": 2000 }],
        "largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
        "cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }],
        "interactive": ["warn", { "maxNumericValue": 3500 }],
        "total-byte-weight": ["warn", { "maxNumericValue": 500000 }]
      }
    }
  }
}

CI Integration

# GitHub Actions
name: Performance Check

on: [pull_request]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Build
        run: npm run build
        
      - name: Lighthouse CI
        uses: treosh/lighthouse-ci-action@v11
        with:
          configPath: './lighthouserc.json'
          uploadArtifacts: true

Monitoring and Measurement

Tools

ToolPurposeData Type
PageSpeed InsightsQuick checkLab + Field
Chrome UX ReportReal user dataField
LighthouseDeep analysisLab
WebPageTestDetailed waterfallLab
RUM (Vercel, Cloudflare)Continuous monitoringField

Key Dashboard Metrics

Track these for your site:

## Weekly Performance Report

| Metric | This Week | Last Week | Target |
|--------|-----------|-----------|--------|
| LCP (p75) | 2.1s | 2.3s | ≤2.5s ✅ |
| CLS (p75) | 0.08 | 0.12 | ≤0.1 ✅ |
| INP (p75) | 180ms | 220ms | ≤200ms ✅ |
| FCP (p75) | 1.4s | 1.5s | ≤1.8s ✅ |
| TTI (lab) | 3.2s | 3.5s | ≤3.8s ✅ |

Complete Checklist

Images

  • Use modern formats (WebP, AVIF)
  • Resize to display size
  • Set width and height attributes
  • Lazy load below-fold images
  • Preload LCP image with fetchpriority=“high”
  • Use srcset for responsive images

Fonts

  • Use font-display: swap
  • Preload critical fonts
  • Use size-adjusted fallbacks
  • Limit font variations
  • Self-host fonts (avoid external requests)

JavaScript

  • Defer non-critical scripts
  • Code-split large bundles
  • Break long tasks (yield every 8–16ms)
  • Use Web Workers for heavy computation
  • Remove unused JavaScript

CSS

  • Inline critical CSS
  • Defer non-critical CSS
  • Remove unused CSS
  • Avoid layout-triggering properties in animations

Server

  • TTFB < 800ms
  • Enable compression (gzip/Brotli)
  • Set cache headers
  • Use CDN
  • HTTP/2 or HTTP/3

Layout Stability

  • Dimensions on all images
  • Reserved space for ads/embeds
  • No content insertion above viewport
  • Smooth transitions for dynamic content

Monitoring

  • Lighthouse CI in deployment pipeline
  • Real user monitoring (RUM)
  • Weekly performance review
  • Alerts for regression

FAQ

Should I optimize for Lighthouse score or Core Web Vitals?

Core Web Vitals (field data). Lighthouse is a diagnostic tool, not the ranking signal. A perfect Lighthouse score with poor field data won’t help SEO.

How do I know if my changes helped?

Compare field data week-over-week. CrUX updates monthly; RUM tools update continuously. Allow 1–2 weeks after changes for meaningful comparison.

What’s the biggest quick win?

Image optimization. Most sites can improve LCP by 30–50% just by properly sizing and formatting images.

How often should I check performance?

Monitor field data weekly. Run Lighthouse on every deploy (via CI). Do deep analysis monthly.

Does performance really affect SEO?

Yes. Core Web Vitals are a ranking factor. Poor performance also increases bounce rate, which indirectly affects rankings. Fast sites convert better too.

What about single-page apps (SPAs)?

SPAs often struggle with INP. Use route-level code splitting, minimize initial bundle, and ensure transitions don’t block main thread.

Sources & Further Reading

Interested in our research?

We share our work openly. If you'd like to collaborate or discuss ideas — we'd love to hear from you.

Get in Touch

Let's build
something real.

No more slide decks. No more "maybe next quarter".
Let's ship your MVP in weeks.

Start Building Now