A

Performance Overview

performanceweb-vitalsoptimizationuser-experience

Performance Overview

Web performance determines whether users stay or leave. A one-second delay in page load can reduce conversions by 7%, and 53% of mobile users abandon sites that take longer than three seconds to load. Performance isn't just a technical metric—it's a direct line to revenue, retention, and search rankings.

This section covers the metrics that matter, the strategies that work, and the tools you need to diagnose and fix performance problems in modern web applications.

Why Performance Matters

Business Impact

The data is consistent across industries:

  • Amazon found that every 100ms of latency cost them 1% in sales
  • Google discovered that a 0.5-second delay in search results caused a 20% drop in traffic
  • Pinterest increased search engine traffic by 15% after reducing perceived wait time by 40%

Performance directly affects:

Metric Impact
Conversion Rate 2x higher for sites loading in 2s vs 5s
Bounce Rate +32% when load time goes from 1s to 3s
SEO Rankings Core Web Vitals are a ranking factor since 2021
Ad Revenue Faster pages = more pageviews = more impressions

Mobile Reality

Over 60% of web traffic is mobile, often on 3G/4G connections with high latency. Your blazing-fast development machine doesn't represent your users:

Your MacBook Pro:     ~50ms latency, 1Gbps
Average mobile user:  ~100-300ms latency, 10-50Mbps (effective)
Emerging markets:     ~300-600ms latency, 2-10Mbps

Always test on throttled connections. Chrome DevTools' "Slow 3G" preset (400ms RTT, 400kbps) is a good stress test.

Perceived vs. Actual Performance

A page that loads in 3 seconds can feel faster than one loading in 2 seconds. Perception matters more than stopwatch measurements.

The Psychology of Waiting

Users perceive wait time differently based on:

  1. Active vs. passive waiting: Progress indicators make waits feel shorter
  2. Known vs. unknown duration: "Loading 3 of 5 images" beats a spinner
  3. Occupied vs. unoccupied time: Show content progressively, not all at once

Skeleton Screens vs. Spinners

Spinners tell users "wait." Skeleton screens tell users "content is coming."

<!-- Spinner: User stares at nothing -->
<div class="loading-spinner"></div>

<!-- Skeleton: User sees the shape of content -->
<div class="skeleton">
  <div class="skeleton-avatar"></div>
  <div class="skeleton-text"></div>
  <div class="skeleton-text short"></div>
</div>
.skeleton {
  background: linear-gradient(
    90deg,
    #f0f0f0 25%,
    #e0e0e0 50%,
    #f0f0f0 75%
  );
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
}

@keyframes shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

Facebook, LinkedIn, and YouTube all use skeleton screens because they reduce perceived load time by 10-20%, even when actual load time is unchanged.

Optimistic UI Updates

Don't wait for the server to confirm actions the user just performed:

// ❌ Slow: Wait for server before updating UI
async function likePost(postId: string) {
  const response = await fetch(`/api/posts/${postId}/like`, { method: 'POST' });
  if (response.ok) {
    setLiked(true); // UI updates after network round-trip
  }
}

// ✅ Fast: Update UI immediately, rollback on error
async function likePost(postId: string) {
  setLiked(true); // Instant feedback
  try {
    await fetch(`/api/posts/${postId}/like`, { method: 'POST' });
  } catch {
    setLiked(false); // Rollback on failure
    showToast('Failed to like post');
  }
}

The 3-Second Rule

Three seconds is the threshold where most users give up. Your performance budget should work backward from this goal.

Setting a Performance Budget

A budget allocates your 3-second window across different resource types:

Resource Type Budget Rationale
HTML 50KB First byte should be fast
CSS (critical) 30KB Render-blocking, must be small
JavaScript (initial) 150KB Main thread blocking
Images (above fold) 200KB LCP optimization
Fonts 100KB 2-3 font files max
Total ~530KB Leaves room for network variance

Enforcement

Automate budget enforcement in your build process:

// vite.config.ts - Build warnings for large chunks
export default defineConfig({
  build: {
    chunkSizeWarningLimit: 150, // KB
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          // Keep vendor bundle separate and cacheable
        }
      }
    }
  }
});
// bundlesize.config.json - CI enforcement
{
  "files": [
    { "path": "dist/assets/*.js", "maxSize": "150 kB" },
    { "path": "dist/assets/*.css", "maxSize": "30 kB" },
    { "path": "dist/index.html", "maxSize": "50 kB" }
  ]
}

Tools like bundlesize or size-limit can fail CI builds when budgets are exceeded.

Performance Optimization Strategies

Performance work falls into three categories:

1. Reduce Bytes

Less data = faster transfer:

  • Minify JavaScript and CSS (handled by Vite, webpack, etc.)
  • Compress with Brotli/gzip at the server level
  • Use modern image formats (WebP, AVIF)
  • Remove unused code (tree shaking, dead code elimination)
  • Subset fonts to only required characters

See the Asset Optimization article for detailed techniques.

2. Reduce Requests

Fewer round-trips = lower latency impact:

  • Bundle CSS and JavaScript appropriately
  • Inline critical CSS in <head>
  • Use HTTP/2 or HTTP/3 (multiplexing reduces connection overhead)
  • Combine icon sprites or use inline SVG
  • Leverage browser caching aggressively

See the Caching Strategies article for header configuration.

3. Reduce Blocking

Unblock the render path:

  • Defer non-critical JavaScript with async or defer
  • Lazy load below-the-fold images
  • Use font-display: swap to prevent invisible text
  • Avoid layout shifts (reserve space for dynamic content)
  • Split code by route—don't ship code users won't need

See the Loading Strategies article for implementation patterns.

The Critical Rendering Path

Understanding how browsers render pages helps you optimize effectively:

1. HTML Download
   ↓
2. Parse HTML → Build DOM
   ↓ (blocked by CSS)
3. Parse CSS → Build CSSOM
   ↓
4. Combine DOM + CSSOM → Render Tree
   ↓ (blocked by JS if not deferred)
5. Layout (calculate positions)
   ↓
6. Paint (pixels to screen)

Key insight: CSS and synchronous JavaScript block rendering. This is why:

  • Critical CSS should be inlined
  • Non-critical CSS should use media queries or be loaded asynchronously
  • JavaScript should use defer (preferred) or async
<!-- Optimal resource loading order -->
<head>
  <!-- Critical CSS inline -->
  <style>/* above-the-fold styles */</style>

  <!-- Preload important resources -->
  <link rel="preload" href="/fonts/inter.woff2" as="font" crossorigin>
  <link rel="preload" href="/hero.webp" as="image">

  <!-- Non-critical CSS loaded async -->
  <link rel="preload" href="/styles/main.css" as="style" onload="this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="/styles/main.css"></noscript>

  <!-- JavaScript deferred -->
  <script src="/app.js" defer></script>
</head>

Quick Wins Checklist

Before diving into complex optimizations, ensure you've covered the basics:

  • Enable gzip/Brotli compression on your server
  • Set appropriate Cache-Control headers
  • Use a CDN for static assets
  • Add loading="lazy" to below-fold images
  • Add defer to non-critical scripts
  • Set explicit width and height on images
  • Serve WebP images with JPEG/PNG fallbacks
  • Subset and self-host fonts
  • Enable HTTP/2 on your server
  • Remove unused CSS and JavaScript

These fundamentals often provide 50-70% of potential performance gains.

Measuring Performance

You can't improve what you don't measure. Key tools:

  • Lab data: Lighthouse, WebPageTest, Chrome DevTools
  • Field data: Chrome User Experience Report (CrUX), Real User Monitoring (RUM)

Lab data is consistent and reproducible. Field data reflects real user experience. Both are valuable.

See the Profiling & Debugging article for detailed tool usage.

Next Steps

This section covers:

  1. Core Web Vitals: The metrics Google uses for ranking—LCP, CLS, and INP
  2. Loading Strategies: Code splitting, lazy loading, and resource hints
  3. Asset Optimization: Images, fonts, and other static resources
  4. Caching Strategies: Browser, CDN, and server-side caching
  5. Profiling & Debugging: Finding and fixing performance bottlenecks

See Also

  • Vite - Modern build tool with excellent code splitting defaults
  • Docker Deployment - Configuring compression and caching at the container level
  • Astro - Framework designed for performance with partial hydration

Last updated: March 23, 2026