TopFrontDev logo
HomeVideosPlaylistsResourcesArticlesAbout
Youtube logoSubscribe

TopFrontDev

Front-end techniques that actually ship. Practical engineering you can use in production.

Content

  • Videos
  • Playlists
  • Articles
  • Resources

About

  • About
  • Sponsorship
  • Contact

Connect

Youtube logogithub logolinkedin logo

© 2026 TopFrontDev. All rights reserved.

TopFrontDev logo
HomeVideosPlaylistsResourcesArticlesAbout
Youtube logoSubscribe

Web Performance Optimization in 2025: Core Web Vitals & Beyond

October 25, 2025

Performance optimization in 2025 requires a holistic approach combining Core Web Vitals excellence with modern loading strategies, advanced caching, and comprehensive monitoring. This guide covers everything you need to deliver exceptional user experiences.

Core Web Vitals 2025: The Complete Picture

Google's Core Web Vitals evolved in 2024, with INP replacing FID as the primary interaction metric. Here's the complete breakdown:

Largest Contentful Paint (LCP)

Target: < 2.5 seconds (75th percentile)

LCP measures when the largest content element becomes visible. Focus on:

  • Optimizing server response times
  • Removing render-blocking JavaScript and CSS
  • Optimizing web fonts
  • Delivering content via CDN
// Critical resource optimization
<link rel="preload" href="/hero-image.webp" as="image" fetchpriority="high">
<link rel="preload" href="/critical-font.woff2" as="font" type="font/woff2" crossorigin>

// Next.js Image component with priority
import Image from 'next/image'

<Image
  src="/hero.jpg"
  alt="Hero"
  width={1200}
  height={600}
  priority
  fetchPriority="high"
  placeholder="blur"
/>

Interaction to Next Paint (INP)

Target: < 200 milliseconds (75th percentile)

INP measures responsiveness by tracking the longest interaction delay. Replace FID with INP optimization:

// Break up long tasks with scheduler/yield
function processLargeDataset(data) {
  const chunks = chunkArray(data, 50);

  async function processChunk(index = 0) {
    if (index >= chunks.length) return;

    // Allow browser to handle user interactions
    await scheduler.yield();

    const chunk = chunks[index];
    await processChunkData(chunk);

    return processChunk(index + 1);
  }

  return processChunk();
}

// React 18+ automatic batching
function handleMultipleUpdates() {
  setState1(newValue1); // Batched automatically
  setState2(newValue2); // No extra re-render
  setState3(newValue3);
}

Cumulative Layout Shift (CLS)

Target: < 0.1 (75th percentile)

CLS measures visual stability. Prevent layout shifts by reserving space:

/* Modern aspect-ratio for images */
.hero-image {
  aspect-ratio: 16 / 9;
  width: 100%;
  height: auto; /* Browser calculates height */
}

/* Font loading optimization */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-var.woff2') format('woff2-variations');
  font-display: swap; /* Prevent invisible text */
}

/* Reserve space for dynamic content */
.skeleton {
  min-height: 200px; /* Reserve space while loading */
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: loading 1.5s infinite;
}

Advanced Loading Strategies

Modern Code Splitting Patterns

// Route-based splitting (Next.js automatic)
app/
├── (marketing)/       // Marketing pages bundle
│   ├── page.tsx
│   └── about/page.tsx
└── (dashboard)/       // Dashboard bundle
    ├── page.tsx
    └── settings/page.tsx

// Component-level splitting with React.lazy
const HeavyChart = lazy(() =>
  import('./components/HeavyChart')
);

// Vendor splitting
import { createRoot } from 'react-dom/client'; // React in separate chunk
import { createStore } from 'redux';          // Redux in separate chunk

Speculative Loading

// Preload likely next pages
<link rel="prefetch" href="/dashboard" as="document">

// Module preloading
<link rel="modulepreload" href="/dashboard.js">

// Speculative rules (Chrome 121+)
<script type="speculationrules">
{
  "prerender": [{
    "source": "list",
    "urls": ["/dashboard", "/profile"]
  }]
}
</script>

Critical Resource Optimization

<!-- Preload critical resources -->
<link rel="preload" href="/critical.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<link rel="preload" href="/app.js" as="script">

<!-- DNS prefetch for third parties -->
<link rel="dns-prefetch" href="//analytics.example.com">
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>

Image Optimization Revolution

Next-Gen Formats & Responsive Images

// Next.js advanced image optimization
<Image
  src="/photo.avif"
  alt="Description"
  width={800}
  height={600}
  sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
  quality={85}
  loading="lazy"
  placeholder="blur"
  blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ..."
/>

// Picture element for art direction
<picture>
  <source media="(min-width: 800px)" srcset="hero-large.avif">
  <source media="(min-width: 400px)" srcset="hero-medium.webp">
  <img src="hero-small.jpg" alt="Hero image" loading="eager">
</picture>

Modern Image Formats

# Convert to WebP/AVIF
npx sharp input.jpg --webp --avif -o output

# Automate with build tools
// next.config.mjs
/** @type {import('next').NextConfig} */
const config = {
  images: {
    formats: ['image/avif', 'image/webp'],
    minimumCacheTTL: 60,
  },
};

export default config;

JavaScript Optimization Strategies

Bundle Analysis & Tree Shaking

# Analyze bundle size
npm run build -- --analyze

# Webpack Bundle Analyzer
npm install --save-dev webpack-bundle-analyzer

# Identify unused exports
npx unimported

Modern JavaScript Patterns

// Dynamic imports with error boundaries
const LazyComponent = lazy(() =>
  import('./HeavyComponent').catch(err => {
    console.error('Failed to load component:', err);
    return { default: () => <div>Failed to load</div> };
  })
);

// Web Workers for heavy computations
const worker = new Worker('/calculation-worker.js');

// Offscreen Canvas for graphics
const canvas = document.createElement('canvas');
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker('/render-worker.js');
worker.postMessage({ canvas: offscreen }, [offscreen]);

Advanced Caching Strategies

HTTP Caching Headers

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

# API responses
Cache-Control: public, max-age=300, stale-while-revalidate=86400

Service Worker Caching

// Cache-first strategy for static assets
self.addEventListener('fetch', (event) => {
  if (event.request.destination === 'script' ||
      event.request.destination === 'style') {
    event.respondWith(
      caches.match(event.request)
        .then(response => response || fetch(event.request))
    );
  }
});

// Background sync for offline functionality
self.addEventListener('sync', (event) => {
  if (event.tag === 'background-sync') {
    event.waitUntil(doBackgroundSync());
  }
});

Compression & Network Optimization

Modern Compression

# Enable Brotli (better than Gzip)
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/manifest+json;

# Fallback to Gzip
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

Resource Prioritization

<!-- High priority resources -->
<link rel="preload" href="/critical.css" as="style" fetchpriority="high">
<link rel="preload" href="/hero-image.webp" as="image" fetchpriority="high">

<!-- Low priority resources -->
<link rel="prefetch" href="/non-critical.js" as="script" fetchpriority="low">

Performance Monitoring & Analytics

Web Vitals Library Integration

// Install: npm install web-vitals
import { onCLS, onINP, onLCP } from 'web-vitals';

function sendToAnalytics({ name, value, id, delta }) {
  // Use sendBeacon for reliable delivery
  navigator.sendBeacon('/analytics/web-vitals', JSON.stringify({
    name,
    value: Math.round(value),
    id,
    timestamp: Date.now()
  }));
}

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

// Additional metrics
import { onFCP, onTTFB } from 'web-vitals';
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);

Real User Monitoring (RUM)

// Custom performance observer
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.duration > 100) { // Long task
      reportLongTask(entry);
    }
  }
});

observer.observe({ type: 'longtask', buffered: true });

// Layout shift tracking
const layoutObserver = new PerformanceObserver((list) => {
  let clsValue = 0;
  for (const entry of list.getEntries()) {
    if (!entry.hadRecentInput) {
      clsValue += entry.value;
    }
  }
  reportCLS(clsValue);
});

layoutObserver.observe({ type: 'layout-shift', buffered: true });

Performance Budgets & CI/CD

Lighthouse CI Integration

# .github/workflows/performance.yml
name: Performance Checks
on: [pull_request]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Run Lighthouse
        run: |
          npm install -g lighthouse
          lighthouse https://your-site.com \
            --output json \
            --output-path ./lighthouse-results.json \
            --budget-path ./.lighthouserc.js

      - name: Check performance budget
        run: |
          node scripts/check-performance-budget.js

Performance Budget Configuration

// .lighthouserc.js
module.exports = {
  ci: {
    collect: {
      numberOfRuns: 3,
      startServerCommand: 'npm run start',
      url: ['http://localhost:3000']
    },
    assert: {
      assertions: {
        'categories:performance': ['error', { minScore: 0.9 }],
        'categories:accessibility': ['error', { minScore: 0.9 }],
        'categories:best-practices': ['error', { minScore: 0.9 }],
        'categories:seo': ['error', { minScore: 0.9 }]
      }
    },
    upload: {
      target: 'temporary-public-storage'
    }
  }
};

Advanced Optimization Techniques

Critical CSS Extraction

// Extract critical CSS
const critical = require('critical');

critical.generate({
  inline: true,
  base: 'dist/',
  src: 'index.html',
  dest: 'index-critical.html',
  minify: true,
  extract: false,
  dimensions: [{
    width: 375,
    height: 667
  }, {
    width: 1920,
    height: 1080
  }]
});

JavaScript Optimization

// Webpack configuration for production
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
        framework: {
          chunks: 'all',
          name: 'framework',
          test: /(?<!node_modules.*)[\\/]node_modules[\\/](react|react-dom|scheduler|prop-types|use-subscription)[\\/]/,
          priority: 40,
        },
      },
    },
  },
};

2025 Performance Checklist

Core Web Vitals ✅

  • LCP < 2.5s (optimize hero images, server response)
  • INP < 200ms (break up long tasks, optimize event handlers)
  • CLS < 0.1 (reserve space, avoid layout shifts)

Loading Performance ✅

  • Enable compression (Brotli > Gzip)
  • Implement code splitting (routes, components, vendors)
  • Optimize images (WebP/AVIF, responsive, lazy loading)
  • Use resource hints (preload, prefetch, preconnect)

JavaScript Optimization ✅

  • Minimize bundle size (tree shaking, compression)
  • Implement lazy loading for non-critical code
  • Optimize third-party scripts (async, defer)
  • Use Web Workers for heavy computations

Caching Strategy ✅

  • Implement proper HTTP caching headers
  • Use Service Worker for offline functionality
  • Implement effective CDN strategy
  • Cache API responses appropriately

Monitoring & Analytics ✅

  • Track Core Web Vitals with web-vitals library
  • Implement Real User Monitoring (RUM)
  • Set up performance budgets
  • Monitor long tasks and layout shifts

Advanced Techniques ✅

  • Implement Critical CSS inlining
  • Use modern image formats and responsive images
  • Optimize web fonts (preload, display: swap)
  • Implement background sync for offline functionality

Conclusion

Web performance optimization in 2025 is about delivering exceptional user experiences through every layer of your application. Core Web Vitals provide the foundation, but modern loading strategies, advanced caching, and comprehensive monitoring ensure your application performs beautifully across all devices and network conditions.

Start with Core Web Vitals as your north star, then layer on advanced optimizations. Remember: performance is a feature, not an afterthought. Your users will thank you for it!

What performance optimization challenge are you tackling next? Share your strategies and results in the comments.

Code Splitting

Dynamic Imports

// Next.js
const DynamicComponent = dynamic(() => import('./HeavyComponent'), {
  loading: () => <Skeleton />,
});

// React
const HeavyComponent = lazy(() => import('./HeavyComponent'));

Route-Based Splitting

// Automatically splits by route in Next.js
app/
├── dashboard/
│   └── page.tsx  // Separate bundle
└── settings/
    └── page.tsx  // Separate bundle

Image Optimization

import Image from 'next/image';

// Modern formats, responsive, lazy loaded
<Image
  src="/photo.jpg"
  alt="Description"
  width={800}
  height={600}
  loading="lazy"
  placeholder="blur"
  blurDataURL="data:image/jpeg;base64,..."
/>

Font Optimization

// next/font automatically optimizes fonts
import { Inter } from 'next/font/inter';

const inter = Inter({
  subsets: ['latin'],
  display: 'swap', // Prevent FOIT
  preload: true,
});

Resource Hints

<!-- Preconnect to external domains -->
<link rel="preconnect" href="https://api.example.com">

<!-- Preload critical resources -->
<link rel="preload" href="/font.woff2" as="font" type="font/woff2" crossorigin>

<!-- Prefetch future navigation -->
<link rel="prefetch" href="/dashboard">

Bundle Analysis

# Analyze your bundle
npm run build -- --analyze

# Look for:
# - Large dependencies
# - Duplicate packages
# - Unused code

Monitoring

// Real User Monitoring
export function reportWebVitals(metric) {
  if (metric.label === 'web-vital') {
    // Send to analytics
    gtag('event', metric.name, {
      value: Math.round(metric.value),
      event_label: metric.id,
    });
  }
}

Checklist

  • Optimize images (format, size, lazy loading)
  • Implement code splitting
  • Minimize JavaScript bundle
  • Use CDN for static assets
  • Enable compression (Brotli/Gzip)
  • Implement caching strategy
  • Monitor Core Web Vitals
  • Use performance budgets

Conclusion

Performance optimization is an ongoing process. Use these techniques to ensure your application provides an excellent user experience.

TopFrontDev

Front-end techniques that actually ship. Practical engineering you can use in production.

Content

  • Videos
  • Playlists
  • Articles
  • Resources

About

  • About
  • Sponsorship
  • Contact

Connect

Youtube logogithub logolinkedin logo

© 2026 TopFrontDev. All rights reserved.