Web Application Performance Optimization Guide

PerformanceCore Web VitalsOptimizationWeb DevelopmentJavaScriptCaching

Overview

Web application performance optimization is essential for improving user experience and SEO rankings. This guide comprehensively covers the latest performance optimization techniques for 2025, including Core Web Vitals improvements, bundle optimization, and caching strategies.

Details

Core Web Vitals Optimization

Core Web Vitals are three critical metrics defined by Google that measure the user experience of web pages.

LCP (Largest Contentful Paint)

  • Good: Within 2.5 seconds
  • Needs Improvement: Between 2.5 and 4.0 seconds
  • Poor: Over 4.0 seconds

Optimization techniques:

  • Use fetchpriority="high" for critical resources
  • Adopt WebP/AVIF formats and implement responsive images
  • Leverage CDN and improve server performance
  • Eliminate render-blocking resources

INP (Interaction to Next Paint)

  • Good: Under 200 milliseconds
  • Needs Improvement: Between 200 and 500 milliseconds
  • Poor: Over 500 milliseconds

Optimization techniques:

  • Optimize JavaScript execution and break down long tasks
  • Reduce third-party scripts
  • Lighten event handlers
  • Prioritize critical UI elements

CLS (Cumulative Layout Shift)

  • Good: Less than 0.1
  • Needs Improvement: Between 0.1 and 0.25
  • Poor: Above 0.25

Optimization techniques:

  • Always specify dimensions for images, videos, and ads
  • Avoid injecting dynamic content above existing content
  • Implement lazy loading properly

JavaScript Bundle Optimization

Code Splitting

A technique that breaks down applications into smaller chunks, loading only necessary parts on demand.

Implementation with Webpack:

  • Utilize dynamic imports (import())
  • Extract common dependencies with SplitChunksPlugin
  • Fine control with Magic Comments

Implementation with Vite:

  • Native ES module support
  • Automatic <link rel="modulepreload"> directive generation
  • CSS splitting control with build.cssCodeSplit

Lazy Loading

Defer non-essential resources until needed to reduce initial load time.

Best practices:

  • Start with route-based code splitting
  • Avoid over-splitting (balance with HTTP request count)
  • Preload critical resources with preload attribute
  • Apply to conditionally rendered components

Image Optimization

Modern Image Formats

AVIF:

  • Up to 50% smaller than JPEG, 20-30% smaller than WebP
  • Superior compression efficiency and quality
  • Wide Color Gamut (WCG) and High Dynamic Range (HDR) support

WebP:

  • 25-34% file size reduction compared to JPEG
  • Wider browser support
  • Better encoding/decoding speed than AVIF

Implementation Pattern

<picture>
  <source srcset="/path/to/img.avif" type="image/avif">
  <source srcset="/path/to/img.webp" type="image/webp">
  <img src="/path/to/img.jpeg" alt="" loading="lazy">
</picture>

Caching Strategies

Service Worker Caching

  • Cache First: Responsive but not updated
  • Stale While Revalidate: Responsive with moderate freshness
  • Network First: Optimal for real-time data
  • Network Only: When always fresh data is required

Layered Caching Architecture

  1. Service Worker cache
  2. HTTP cache (browser cache)
  3. CDN cache
  4. Origin server

Cache Header Configuration

Cache-Control: max-age=3600, stale-while-revalidate=86400

Performance Monitoring

Real User Monitoring (RUM)

Collect performance data from actual users and continuously monitor Core Web Vitals.

Major RUM tools:

  • DebugBear: Core Web Vitals focused
  • RUMvision: 24/7 site speed monitoring
  • Datadog RUM: Comprehensive visibility
  • SpeedCurve: User experience optimization

Third-party Script Optimization

  • Evaluate external script impact
  • Implement asynchronous loading
  • Remove unnecessary scripts
  • Set performance budgets

Pros & Cons

Pros

  • Improved User Experience: Reduced bounce rates through faster page loads
  • Better SEO Rankings: Core Web Vitals are Google ranking factors
  • Higher Conversion Rates: Yahoo! JAPAN achieved 15.1% increase in page views
  • Reduced Bandwidth: Up to 70% data transfer reduction through optimization
  • Enhanced Mobile Experience: Significant improvements especially on slow networks

Cons

  • Implementation Complexity: Requires understanding and implementing numerous optimization techniques
  • Increased Development Time: Initial setup and testing take time
  • Browser Compatibility: Latest technologies may not work on older browsers
  • Ongoing Maintenance: Requires continuous monitoring and adjustment of performance metrics
  • Complex Resource Management: Managing caching strategies and versioning

References

Examples

Core Web Vitals Measurement

// Measuring Core Web Vitals using web-vitals library
import {getCLS, getFID, getLCP, getINP} from 'web-vitals';

function sendToAnalytics(metric) {
  // Example sending to Google Analytics 4
  gtag('event', metric.name, {
    value: Math.round(metric.value),
    metric_id: metric.id,
    metric_value: metric.value,
    metric_delta: metric.delta,
  });
}

// Measure each metric
getCLS(sendToAnalytics);
getLCP(sendToAnalytics);
getINP(sendToAnalytics);

Webpack Code Splitting

// webpack.config.js
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10
        },
        common: {
          minChunks: 2,
          priority: 5,
          reuseExistingChunk: true
        }
      }
    }
  }
};

// Using dynamic imports
const loadComponent = async () => {
  const module = await import(
    /* webpackChunkName: "my-component" */
    /* webpackPrefetch: true */
    './MyComponent'
  );
  return module.default;
};

Vite Optimization Configuration

// vite.config.js
import { defineConfig } from 'vite';
import compression from 'vite-plugin-compression';

export default defineConfig({
  build: {
    // Code splitting configuration
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          utils: ['lodash', 'axios']
        }
      }
    },
    // CSS splitting control
    cssCodeSplit: true,
    // Build target
    target: 'es2015'
  },
  plugins: [
    // gzip compression
    compression({
      algorithm: 'gzip',
      ext: '.gz'
    }),
    // Brotli compression
    compression({
      algorithm: 'brotliCompress',
      ext: '.br'
    })
  ]
});

Service Worker Caching Strategy

// service-worker.js
const CACHE_NAME = 'app-v1';
const urlsToCache = [
  '/',
  '/styles/main.css',
  '/scripts/main.js'
];

// Cache on install
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => cache.addAll(urlsToCache))
  );
});

// Stale While Revalidate strategy
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => {
        // Return cache if available
        if (response) {
          // Update in background
          fetch(event.request)
            .then(fetchResponse => {
              caches.open(CACHE_NAME)
                .then(cache => {
                  cache.put(event.request, fetchResponse.clone());
                });
            });
          return response;
        }
        // Fetch from network if not cached
        return fetch(event.request);
      })
  );
});

Image Optimization Implementation

// React component with optimized images
import React from 'react';

const OptimizedImage = ({ src, alt, sizes }) => {
  const filename = src.split('.').slice(0, -1).join('.');
  
  return (
    <picture>
      <source 
        type="image/avif" 
        srcSet={`
          ${filename}.avif 1x,
          ${filename}@2x.avif 2x,
          ${filename}@3x.avif 3x
        `}
        sizes={sizes}
      />
      <source 
        type="image/webp" 
        srcSet={`
          ${filename}.webp 1x,
          ${filename}@2x.webp 2x,
          ${filename}@3x.webp 3x
        `}
        sizes={sizes}
      />
      <img 
        src={src} 
        alt={alt}
        loading="lazy"
        decoding="async"
        sizes={sizes}
      />
    </picture>
  );
};

// Usage example
<OptimizedImage 
  src="/images/hero.jpg" 
  alt="Hero image"
  sizes="(max-width: 768px) 100vw, 50vw"
/>

Performance Monitoring

// RUM implementation example
class PerformanceMonitor {
  constructor(endpoint) {
    this.endpoint = endpoint;
    this.metrics = {};
    this.initializeObservers();
  }

  initializeObservers() {
    // Monitor LCP with PerformanceObserver
    const lcpObserver = new PerformanceObserver((entryList) => {
      const entries = entryList.getEntries();
      const lastEntry = entries[entries.length - 1];
      this.metrics.lcp = lastEntry.renderTime || lastEntry.loadTime;
    });
    lcpObserver.observe({ entryTypes: ['largest-contentful-paint'] });

    // Monitor layout shifts
    const clsObserver = new PerformanceObserver((entryList) => {
      let cls = 0;
      for (const entry of entryList.getEntries()) {
        if (!entry.hadRecentInput) {
          cls += entry.value;
        }
      }
      this.metrics.cls = cls;
    });
    clsObserver.observe({ entryTypes: ['layout-shift'] });

    // Monitor interactions
    const inpObserver = new PerformanceObserver((entryList) => {
      for (const entry of entryList.getEntries()) {
        if (entry.interactionId) {
          this.metrics.inp = Math.max(
            this.metrics.inp || 0,
            entry.duration
          );
        }
      }
    });
    inpObserver.observe({ entryTypes: ['event'] });
  }

  sendMetrics() {
    fetch(this.endpoint, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        url: window.location.href,
        metrics: this.metrics,
        timestamp: Date.now()
      })
    });
  }
}

// Usage example
const monitor = new PerformanceMonitor('/api/metrics');
// Send metrics on page unload
window.addEventListener('pagehide', () => monitor.sendMetrics());