Skip to content
All essays
WebMarch 10, 202412 min

Web Performance Optimization Strategies

Comprehensive guide to optimizing web performance from network requests to rendering efficiency.

Ü
Ümit Uz
Mobile & Full Stack Developer

Performance isn't a luxury—it's essential. Slow sites lose users. Here's how to make yours fast.

Measuring Performance

Core Web Vitals

Google's user-centric performance metrics.

  • LCP (Largest Contentful Paint): < 2.5s
  • FID (First Input Delay): < 100ms
  • CLS (Cumulative Layout Shift): < 0.1

Performance API

Measure real-world performance.

javascript
// Measure LCP
new PerformanceObserver((list) => {
  const entries = list.getEntries();
  const lastEntry = entries[entries.length - 1];
  console.log('LCP:', lastEntry.startTime);
}).observe({ entryTypes: ['largest-contentful-paint'] });

// Measure FID
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('FID:', entry.processingStart - entry.startTime);
  }
}).observe({ entryTypes: ['first-input'] });

// Measure CLS
let clsValue = 0;
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (!entry.hadRecentInput) {
      clsValue += entry.value;
      console.log('CLS:', clsValue);
    }
  }
}).observe({ entryTypes: ['layout-shift'] });

Network Optimization

Reduce HTTP Requests

Bundle resources, combine files when beneficial.

javascript
// Bad: Multiple requests
import _ from 'lodash';
import debounce from 'lodash/debounce';

// Good: Single request
import { debounce } from 'lodash';

HTTP/2 and HTTP/3

Leverage multiplexing and server push.

nginx
# Enable HTTP/2
listen 443 ssl http2;

# Enable HTTP/3 (QUIC)
listen 443 quic;

CDN Usage

Deliver content from edge locations.

html
<!-- Use CDN for libraries -->
<script src="https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js"></script>

Preconnect and DNS-Prefetch

Warm up connections to important origins.

html
<link rel="preconnect" href="https://api.example.com">
<link rel="dns-prefetch" href="https://static.example.com">

Resource Loading

Lazy Loading

Defer loading of below-the-fold content.

html
<!-- Native lazy loading for images -->
<img src="image.jpg" loading="lazy" alt="...">

<!-- Lazy load iframes -->
<iframe src="video.html" loading="lazy"></iframe>

Code Splitting

Split code into smaller chunks.

javascript
// Route-based splitting
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

// Component-based splitting
const HeavyComponent = lazy(() => import('./HeavyComponent'));

Dynamic Imports

Load modules on demand.

javascript
button.addEventListener('click', async () => {
  const module = await import('./heavy-module.js');
  module.doSomething();
});

Asset Optimization

Image Optimization

Serve appropriately sized, compressed images.

html
<!-- Responsive images with srcset -->
<img
  src="image-800.jpg"
  srcset="image-400.jpg 400w,
          image-800.jpg 800w,
          image-1200.jpg 1200w"
  sizes="(max-width: 600px) 100vw, 50vw"
  loading="lazy"
  alt="..."
>

<!-- Modern formats with fallback -->
<picture>
  <source srcset="image.webp" type="image/webp">
  <source srcset="image.avif" type="image/avif">
  <img src="image.jpg" alt="...">
</picture>

Font Optimization

Use font-display for faster rendering.

css
@font-face {
  font-family: 'Custom Font';
  src: url('font.woff2') format('woff2');
  font-display: swap; /* Prevent FOIT */
  unicode-range: U+0020-007E; /* Subset characters */
}

Compression

Enable Brotli or Gzip compression.

nginx
# Brotli compression
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1000;

# Brotli (better compression)
brotli on;
brotli_types text/plain text/css application/json application/javascript;

Caching Strategies

Cache Headers

Set appropriate cache policies.

nginx
# Static assets - long cache
location /static/ {
  expires: 1y;
  add_header Cache-Control: "public, immutable";
}

# HTML - short cache
location / {
  expires: 1h;
  add_header Cache-Control: "public";
}

Service Workers

Cache assets for offline use.

javascript
// Install event - cache assets
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open('v1').then((cache) => {
      return cache.addAll([
        '/',
        '/styles.css',
        '/app.js',
      ]);
    })
  );
});

// Fetch event - serve from cache
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      return response || fetch(event.request);
    })
  );
});

Rendering Optimization

Reduce Layout Thrashing

Batch DOM reads and writes.

javascript
// Bad - causes layout thrashing
element1.height = 100;
const height1 = element2.offsetHeight;
element1.height = height1;

// Good - batch operations
const height1 = element2.offsetHeight;
const height2 = element3.offsetHeight;
element1.height = height1;
element2.height = height2;

Virtual Scrolling

Render only visible items.

javascript
import { FixedSizeList } from 'react-window';

<FixedSizeList
  height={600}
  itemCount={10000}
  itemSize={50}
  width="100%"
>
  {({ index, style }) => (
    <div style={style}>Item {index}</div>
  )}
</FixedSizeList>

Debouncing and Throttling

Limit expensive operations.

javascript
// Debounce - wait until user stops
const debouncedSearch = debounce((query) => {
  searchAPI(query);
}, 300);

// Throttle - limit execution frequency
const throttledScroll = throttle(() => {
  updatePosition();
}, 100);

JavaScript Optimization

Tree Shaking

Remove unused code.

javascript
// Bad: Import entire library
import _ from 'lodash';

// Good: Import what you need
import { debounce } from 'lodash-es';

Minification

Use production builds.

javascript
// Development
const greeting = "Hello, " + name + "!";

// Production (minified)
const g="Hello, "+name+"!";

Avoid Memory Leaks

Clean up subscriptions and listeners.

javascript
useEffect(() => {
  const subscription = observable.subscribe(data => {
    setData(data);
  });

  return () => {
    subscription.unsubscribe(); // Cleanup
  };
}, []);

CSS Optimization

Critical CSS

Inline above-the-fold styles.

html
<style>
  /* Critical CSS for immediate render */
  .hero { background: blue; color: white; }
</style>
<link rel="stylesheet" href="styles.css">

Reduce Selector Complexity

Simplify selectors.

css
/* Bad - complex selector */
nav ul li a:hover span.icon {}

/* Good - simple selector */
.nav-icon:hover {}

Contain Property

Isolate layout recalculations.

css
.sidebar {
  contain: layout style paint;
}

Monitoring

Real User Monitoring (RUM)

Track actual user performance.

javascript
// Send performance data to analytics
const perfData = performance.getEntriesByType('navigation')[0];
const metrics = {
  loadTime: perfData.loadEventEnd - perfData.fetchStart,
  domContentLoaded: perfData.domContentLoadedEventEnd - perfData.fetchStart,
};

sendToAnalytics(metrics);

Lighthouse CI

Catch performance regressions.

json
{
  "ci": {
    "assert": {
      "assertions": {
        "categories:performance": ["error", { "minScore": 0.9 }]
      }
    }
  }
}

Conclusion

Performance optimization is continuous. Measure first, optimize bottlenecks, verify improvements. Focus on Core Web Vitals, optimize critical rendering path, leverage caching, and monitor real-world performance. Fast sites provide better UX and rank higher in search results.

Related essays

Next essay
Frontend Architecture Best Practices