Skip to main content

What Are Core Web Vitals?

Core Web Vitals are Google’s standardized metrics for measuring user experience on the web. They’re critical for both traditional SEO and AI search visibility because fast, stable pages provide better data for AI platforms to crawl and cite.
AI Search Impact: AI platforms prioritize fast-loading, well-performing sites when selecting sources to cite. Poor Core Web Vitals can hurt your visibility.

The Three Core Web Vitals

1. Largest Contentful Paint (LCP)

What it measures: Loading performance - how long it takes for the main content to load. Target: βœ… Good: ≀ 2.5 seconds | ⚠️ Needs Improvement: 2.5-4.0s | ❌ Poor: > 4.0s
Images are the most common LCP element:Use modern formats:
<picture>
  <source srcset="hero.webp" type="image/webp">
  <source srcset="hero.jpg" type="image/jpeg">
  <img src="hero.jpg" alt="Hero image" loading="lazy">
</picture>
Compress images:
  • Use tools like TinyPNG, ImageOptim, or Squoosh
  • Target: < 100KB for hero images
  • Use appropriate dimensions (don’t load 4K images)
Add size attributes:
<img src="hero.jpg" width="1200" height="600" alt="Hero">
Faster server = faster LCP:Use a CDN:
  • Cloudflare, Fastly, or AWS CloudFront
  • Serve content from locations near users
Enable caching:
# Nginx cache example
location / {
    proxy_cache_valid 200 1h;
    add_header Cache-Control "public, max-age=3600";
}
Optimize database queries:
  • Use indexes
  • Cache frequent queries
  • Use connection pooling
CSS and JavaScript can block page rendering:Inline critical CSS:
<head>
  <style>
    /* Critical CSS for above-the-fold content */
    .hero { display: block; }
    .header { position: fixed; }
  </style>
  
  <!-- Load full CSS asynchronously -->
  <link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
</head>
Defer non-critical JavaScript:
<script src="analytics.js" defer></script>
<script src="chat-widget.js" async></script>
Fonts can significantly impact LCP:
<head>
  <!-- Preload important fonts -->
  <link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin>
  
  <!-- Use font-display: swap -->
  <style>
    @font-face {
      font-family: 'Inter';
      src: url('/fonts/inter-var.woff2') format('woff2');
      font-display: swap; /* Shows fallback font while loading */
    }
  </style>
</head>

2. First Input Delay (FID)

What it measures: Interactivity - how quickly the page responds to user interactions. Target: βœ… Good: ≀ 100ms | ⚠️ Needs Improvement: 100-300ms | ❌ Poor: > 300ms
Heavy JavaScript blocks the main thread:Code splitting:
// Load features on demand
const Analytics = lazy(() => import('./Analytics'));
const ChatWidget = lazy(() => import('./ChatWidget'));
Use web workers:
// Move heavy computations off main thread
const worker = new Worker('processor.js');
worker.postMessage(data);
worker.onmessage = (e) => {
  console.log('Result:', e.data);
};
Tasks over 50ms block interactivity:
// Bad: Blocks main thread
function processLargeArray(items) {
  items.forEach(item => {
    heavyProcessing(item);
  });
}

// Good: Break into chunks
async function processLargeArray(items) {
  for (let i = 0; i < items.length; i++) {
    heavyProcessing(items[i]);
    
    // Yield to browser every 50 items
    if (i % 50 === 0) {
      await new Promise(resolve => setTimeout(resolve, 0));
    }
  }
}
Third-party scripts can hurt FID:Audit third-party scripts:
  • Remove unused analytics/tracking
  • Load non-critical scripts asynchronously
  • Use facade patterns for heavy widgets
<!-- Lazy load third-party widgets -->
<div id="chat-widget" data-src="https://example.com/widget.js">
  <button onclick="loadChatWidget()">Load Chat</button>
</div>

3. Cumulative Layout Shift (CLS)

What it measures: Visual stability - how much content shifts unexpectedly during loading. Target: βœ… Good: ≀ 0.1 | ⚠️ Needs Improvement: 0.1-0.25 | ❌ Poor: > 0.25
Always specify dimensions to prevent layout shifts:
<!-- Bad: No dimensions -->
<img src="hero.jpg" alt="Hero">

<!-- Good: With dimensions -->
<img src="hero.jpg" width="1200" height="600" alt="Hero">

<!-- Best: Use aspect ratio -->
<img src="hero.jpg" 
     width="1200" 
     height="600" 
     style="aspect-ratio: 2/1; width: 100%; height: auto;" 
     alt="Hero">
For responsive images:
.responsive-image {
  aspect-ratio: 16 / 9;
  width: 100%;
  height: auto;
}
Prevent shifts from loading content:
<!-- Reserve space with min-height -->
<div class="banner" style="min-height: 60px;">
  <!-- Banner loaded dynamically -->
</div>

<!-- Use skeleton screens -->
<div class="content-skeleton" aria-hidden="true">
  <div class="skeleton-line"></div>
  <div class="skeleton-line"></div>
  <div class="skeleton-line"></div>
</div>
CSS for skeleton screens:
.skeleton-line {
  height: 20px;
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: loading 1.5s infinite;
  border-radius: 4px;
  margin-bottom: 10px;
}

@keyframes loading {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
Use font-display to prevent layout shifts:
@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom.woff2') format('woff2');
  font-display: swap; /* Show fallback immediately */
}

/* Or use optional for minimal shift */
@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom.woff2') format('woff2');
  font-display: optional; /* Skip if not cached */
}
Avoid adding content that pushes down existing content:
// Bad: Inserting banner above content
document.body.insertAdjacentHTML('afterbegin', bannerHTML);

// Good: Append to designated area with reserved space
const bannerContainer = document.getElementById('banner-container');
if (bannerContainer) {
  bannerContainer.innerHTML = bannerHTML;
}
Ads are common CLS culprits:
<!-- Reserve exact ad slot size -->
<div class="ad-container" style="width: 728px; height: 90px;">
  <!-- Ad loads here -->
</div>

<!-- Or use aspect ratio for responsive -->
<div class="ad-container" style="aspect-ratio: 728/90; width: 100%; max-width: 728px;">
  <!-- Ad loads here -->
</div>

Additional Performance Metrics

Time to First Byte (TTFB)

How quickly your server responds: Target: βœ… Good: ≀ 800ms | ❌ Poor: > 1800ms Improvements:
  • Use a CDN
  • Enable server caching
  • Optimize database queries
  • Use faster hosting
  • Enable HTTP/2 or HTTP/3
# Nginx HTTP/2 configuration
server {
    listen 443 ssl http2;
    ssl_protocols TLSv1.2 TLSv1.3;
}

Total Blocking Time (TBT)

Time when the main thread is blocked: Target: βœ… Good: ≀ 200ms | ❌ Poor: > 600ms Improvements:
  • Minimize JavaScript
  • Code splitting
  • Defer non-critical scripts
  • Use web workers

Speed Index

How quickly content is visually displayed: Target: βœ… Good: ≀ 3.4s | ❌ Poor: > 5.8s Improvements:
  • Optimize critical rendering path
  • Lazy load below-the-fold content
  • Use progressive image loading

Performance Optimization Checklist

1

LCP Optimization

  • βœ… Compress and optimize images
  • βœ… Use modern image formats (WebP, AVIF)
  • βœ… Enable CDN
  • βœ… Defer non-critical CSS/JS
  • βœ… Optimize server response time
2

FID Optimization

  • βœ… Minimize JavaScript execution
  • βœ… Break up long tasks
  • βœ… Use code splitting
  • βœ… Remove unused third-party code
  • βœ… Use web workers for heavy tasks
3

CLS Optimization

  • βœ… Add width/height to all images
  • βœ… Reserve space for ads/embeds
  • βœ… Use font-display: swap
  • βœ… Avoid inserting content above existing content
  • βœ… Use skeleton screens for loading states

Testing Core Web Vitals

Google PageSpeed Insights

Test any URL and get specific recommendations pagespeed.web.dev

Chrome DevTools

Use Lighthouse in Chrome DevTools for detailed analysis

Search Console

View real-user Core Web Vitals data in Google Search Console

WebPageTest

Advanced testing with detailed waterfall charts webpagetest.org

Real User Monitoring (RUM)

Track Core Web Vitals from real users:
// Using web-vitals library
import {onCLS, onFID, onLCP} from 'web-vitals';

function sendToAnalytics({name, value, id}) {
  // Send to your analytics
  fetch('/analytics', {
    method: 'POST',
    body: JSON.stringify({
      metric: name,
      value: value,
      id: id
    })
  });
}

onCLS(sendToAnalytics);
onFID(sendToAnalytics);
onLCP(sendToAnalytics);

Performance Budget

Set performance budgets to maintain good metrics:
{
  "budget": [
    {
      "resourceSizes": [
        { "resourceType": "script", "budget": 300 },
        { "resourceType": "image", "budget": 500 },
        { "resourceType": "stylesheet", "budget": 100 },
        { "resourceType": "document", "budget": 50 },
        { "resourceType": "total", "budget": 1000 }
      ],
      "timings": [
        { "metric": "interactive", "budget": 3000 },
        { "metric": "first-contentful-paint", "budget": 1500 }
      ]
    }
  ]
}

Common Optimization Priorities

πŸ”΄ High Priority - LCP Improvements

Optimize Images

Convert to WebP, compress, add dimensions Typical Impact: -1.0s to -1.5s

Enable CDN

Use Cloudflare or similar CDN service Typical Impact: -0.3s to -0.8s

Defer JavaScript

Load non-critical JS after main content Typical Impact: -0.2s to -0.5s

Optimize Server

Improve hosting, enable caching Typical Impact: -0.2s to -0.4s

🟑 Medium Priority - CLS Improvements

Add Image Dimensions

Specify width/height on all images Typical Impact: -0.05 to -0.08

Reserve Ad Space

Pre-allocate space for dynamic content Typical Impact: -0.03 to -0.06

Font Loading

Use font-display: swap Typical Impact: -0.01 to -0.03

βœ… Maintaining Good FID

Keep FID scores low by:
  • Minimizing JavaScript execution
  • Using code splitting
  • Loading third-party scripts asynchronously
  • Utilizing web workers for heavy tasks

Tools and Resources

Performance Analysis

# Lighthouse CLI
npm install -g lighthouse
lighthouse https://asklantern.com --view

# Web Vitals CLI
npm install -g web-vitals-cli
web-vitals https://asklantern.com

Image Optimization Tools

  • ImageOptim (Mac) - Desktop image optimizer
  • Squoosh - Web-based image compressor
  • Sharp (Node.js) - Automated image processing
  • Cloudinary/Imgix - Image CDN with automatic optimization

Monitoring Services

  • Google Search Console - Free real-user Core Web Vitals data
  • Cloudflare Analytics - Free with Cloudflare CDN
  • Vercel Analytics - For Next.js/Vercel deployments
  • New Relic/Datadog - Enterprise monitoring
Why performance matters for AI:
  • πŸ€– AI crawlers prioritize fast, accessible sites
  • πŸ“Š Better UX signals = higher trust scores
  • ⚑ Faster sites get crawled more frequently
  • 🎯 Performance is a ranking factor for citations
  • πŸ’° Better conversion from AI-driven traffic

Monitor with Lantern

Track how performance improvements affect your AI search visibility:
  • πŸ“ˆ Correlation between Core Web Vitals and AI citations
  • πŸ” Compare performance vs. competitors
  • ⏱️ Track performance trends over time
  • 🎯 Get alerts for performance regressions
Regular monitoring is key: Core Web Vitals should be checked weekly. Set up automated monitoring to catch regressions early.

Next Steps

Back to Technical Optimization

Review all technical optimization guides