Web Performance

Core Web Vitals, optimization techniques, and performance monitoring

Core Web Vitals

LCP (Largest Contentful Paint)

Loading performance - should be under 2.5s

LCP: 1.2s (Good)

FID (First Input Delay)

Interactivity - should be under 100ms

FID: 45ms (Good)

CLS (Cumulative Layout Shift)

Visual stability - should be under 0.1

CLS: 0.05 (Good)

TTFB (Time to First Byte)

Server response time - should be under 600ms

TTFB: 200ms (Good)

FCP (First Contentful Paint)

First content appears - should be under 1.8s

FCP: 1.1s (Good)

Performance Budgets

Total Page Weight

Keep under 1.5MB for mobile

Page size: 1.2MB

JavaScript Bundle

Keep under 300KB gzipped

JS: 250KB (gzipped)

CSS Bundle

Keep under 50KB gzipped

CSS: 35KB (gzipped)

Image Optimization

Use WebP, AVIF formats

image.webp (50% smaller)

HTTP Requests

Minimize to under 50 requests

Requests: 35

Image Optimization

WebP Format

Use modern image formats

<img src="image.webp" alt="Description">

Responsive Images

Serve appropriate sizes

<img srcset="small.jpg 300w, large.jpg 800w" sizes="(max-width: 600px) 300px, 800px">

Lazy Loading

Load images on demand

<img loading="lazy" src="image.jpg">

Image Compression

Optimize file sizes

imagemin --quality=85 image.jpg

CDN Delivery

Use content delivery networks

https://cdn.example.com/images/

JavaScript Optimization

Code Splitting

Split bundles by routes

const Home = lazy(() => import("./Home"))

Tree Shaking

Remove unused code

import { useState } from "react" // Only imports useState

Minification

Compress JavaScript code

terser --compress --mangle script.js

Async Loading

Load non-critical scripts

<script async src="analytics.js"></script>

Service Workers

Cache resources offline

navigator.serviceWorker.register("/sw.js")

CSS Optimization

Critical CSS

Inline above-the-fold styles

<style>/* Critical styles */</style>

CSS Minification

Remove whitespace and comments

cssnano --optimize

Unused CSS Removal

Purge unused styles

purgecss --content src/**/*.html

CSS-in-JS Optimization

Extract critical styles

styled-components extractCritical

CSS Delivery

Optimize loading order

Preload critical CSS, defer non-critical

Performance Monitoring

Lighthouse

Google's performance audit tool

lighthouse https://example.com

WebPageTest

Detailed performance analysis

https://www.webpagetest.org/

PageSpeed Insights

Core Web Vitals analysis

https://pagespeed.web.dev/

Chrome DevTools

Built-in performance tools

F12 → Performance tab

Real User Monitoring

Track actual user experience

web-vitals library

Analytics & Metrics

Google Analytics

Web analytics platform

gtag("event", "web_vitals")

Core Web Vitals API

Programmatic access to metrics

web-vitals.getCLS(console.log)

Performance Observer

Monitor performance metrics

new PerformanceObserver(callback)

Resource Timing API

Track resource loading times

performance.getEntriesByType("resource")

Navigation Timing API

Page load timing data

performance.timing.loadEventEnd

Common Patterns

Performance Monitoring Setup

Basic performance monitoring implementation

// web-vitals.js
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';

function sendToAnalytics(metric) {
  // Send to Google Analytics
  gtag('event', metric.name, {
    value: Math.round(metric.value),
    event_category: 'Web Vitals',
    event_label: metric.id,
    non_interaction: true,
  });
}

// Monitor Core Web Vitals
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);

// Custom performance monitoring
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('Performance entry:', entry.name, entry.startTime);
  }
});

observer.observe({ entryTypes: ['navigation', 'resource', 'paint'] });

Image Optimization Component

React component for optimized images

// OptimizedImage.jsx
import { useState, useEffect } from 'react';

const OptimizedImage = ({ 
  src, 
  alt, 
  width, 
  height, 
  sizes = "100vw",
  priority = false 
}) => {
  const [imageSrc, setImageSrc] = useState(src);
  const [isLoaded, setIsLoaded] = useState(false);

  useEffect(() => {
    // Check if WebP is supported
    const webpSupported = document.createElement('canvas')
      .toDataURL('image/webp')
      .indexOf('data:image/webp') === 0;

    if (webpSupported && src.includes('.jpg')) {
      setImageSrc(src.replace('.jpg', '.webp'));
    }
  }, [src]);

  return (
    <div className="image-container">
      <img
        src={imageSrc}
        alt={alt}
        width={width}
        height={height}
        sizes={sizes}
        loading={priority ? 'eager' : 'lazy'}
        onLoad={() => setIsLoaded(true)}
        className={`optimized-image ${isLoaded ? 'loaded' : ''}`}
        style={{
          opacity: isLoaded ? 1 : 0,
          transition: 'opacity 0.3s ease-in-out'
        }}
      />
      {!isLoaded && (
        <div 
          className="image-placeholder"
          style={{ width, height, backgroundColor: '#f0f0f0' }}
        />
      )}
    </div>
  );
};

export default OptimizedImage;

Service Worker for Caching

Basic service worker implementation

// sw.js
const CACHE_NAME = 'app-cache-v1';
const urlsToCache = [
  '/',
  '/styles/main.css',
  '/scripts/main.js',
  '/images/logo.png'
];

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

// Fetch event - Cache First strategy
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((response) => {
        // Return cached version or fetch from network
        return response || fetch(event.request);
      })
  );
});

// Update cache when new version is available
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.map((cacheName) => {
          if (cacheName !== CACHE_NAME) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

// Register service worker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .then((registration) => {
        console.log('SW registered:', registration);
      })
      .catch((error) => {
        console.log('SW registration failed:', error);
      });
  });
}

Critical CSS Extraction

Extract and inline critical CSS

// critical-css.js
import { extractCritical } from 'styled-components';

// Extract critical CSS from styled-components
const criticalCSS = extractCritical(html);

// Inline critical CSS in HTML head
const htmlWithCriticalCSS = `
<!DOCTYPE html>
<html>
<head>
  <style id="critical-css">${criticalCSS.css}</style>
  <link rel="preload" href="/styles/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="/styles/main.css"></noscript>
</head>
<body>
  ${criticalCSS.html}
</body>
</html>
`;

// Alternative: Use a critical CSS tool
// npm install critical
const critical = require('critical');

critical.generate({
  src: 'index.html',
  target: {
    css: 'critical.css',
    html: 'index-critical.html'
  },
  width: 1300,
  height: 900
});

Bundle Analysis and Optimization

Analyze and optimize JavaScript bundles

// webpack.config.js
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
        common: {
          name: 'common',
          minChunks: 2,
          chunks: 'all',
          enforce: true
        }
      }
    },
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true,
            drop_debugger: true
          }
        }
      })
    ]
  },
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      openAnalyzer: false
    })
  ]
};

// Analyze bundle size
// npm install --save-dev webpack-bundle-analyzer
// npx webpack-bundle-analyzer dist/stats.json

// Tree shaking with ES modules
import { useState, useEffect } from 'react'; // Only imports what's used
// Instead of: import * as React from 'react';

Performance Budget Configuration

Set up performance budgets in build tools

// .budgetrc
{
  "connection": {
    "rtt": 50,
    "downlink": 10,
    "effectiveConnectionType": "4g"
  },
  "budgets": [
    {
      "resourceType": "script",
      "budget": 300
    },
    {
      "resourceType": "total",
      "budget": 1500
    },
    {
      "resourceType": "image",
      "budget": 500
    }
  ]
}

// webpack.config.js with performance budgets
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = {
  performance: {
    hints: 'warning',
    maxEntrypointSize: 300000,
    maxAssetSize: 300000,
    assetFilter: function(assetFilename) {
      return !assetFilename.endsWith('.map');
    }
  }
};

// Lighthouse CI configuration
// .lighthouserc.js
module.exports = {
  ci: {
    collect: {
      url: ['http://localhost:3000'],
      numberOfRuns: 3
    },
    assert: {
      assertions: {
        'categories:performance': ['warn', { minScore: 0.9 }],
        'first-contentful-paint': ['warn', { maxNumericValue: 2000 }],
        'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
        'first-input-delay': ['error', { maxNumericValue: 100 }]
      }
    }
  }
};

Tips & Best Practices

Set up performance budgets and monitor them in CI/CD

Use WebP/AVIF images and implement lazy loading

Implement code splitting and tree shaking

Use service workers for caching and offline functionality

Monitor Core Web Vitals in production