Identify and fix performance issues to create faster, smoother user experiences.
Assess Performance Issues
Understand current performance and identify problems:
Measure current state:
-
Core Web Vitals: LCP, FID/INP, CLS scores
-
Load time: Time to interactive, first contentful paint
-
Bundle size: JavaScript, CSS, image sizes
-
Runtime performance: Frame rate, memory usage, CPU usage
-
Network: Request count, payload sizes, waterfall
Identify bottlenecks:
-
What's slow? (Initial load? Interactions? Animations?)
-
What's causing it? (Large images? Expensive JavaScript? Layout thrashing?)
-
How bad is it? (Perceivable? Annoying? Blocking?)
-
Who's affected? (All users? Mobile only? Slow connections?)
CRITICAL: Measure before and after. Premature optimization wastes time. Optimize what actually matters.
Optimization Strategy
Create systematic improvement plan:
Loading Performance
Optimize Images:
-
Use modern formats (WebP, AVIF)
-
Proper sizing (don't load 3000px image for 300px display)
-
Lazy loading for below-fold images
-
Responsive images (srcset , picture element)
-
Compress images (80-85% quality is usually imperceptible)
-
Use CDN for faster delivery
<img src="hero.webp" srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w" sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px" loading="lazy" alt="Hero image" />
Reduce JavaScript Bundle:
-
Code splitting (route-based, component-based)
-
Tree shaking (remove unused code)
-
Remove unused dependencies
-
Lazy load non-critical code
-
Use dynamic imports for large components
// Lazy load heavy component const HeavyChart = lazy(() => import('./HeavyChart'));
Optimize CSS:
-
Remove unused CSS
-
Critical CSS inline, rest async
-
Minimize CSS files
-
Use CSS containment for independent regions
Optimize Fonts:
-
Use font-display: swap or optional
-
Subset fonts (only characters you need)
-
Preload critical fonts
-
Use system fonts when appropriate
-
Limit font weights loaded
@font-face { font-family: 'CustomFont'; src: url('/fonts/custom.woff2') format('woff2'); font-display: swap; /* Show fallback immediately / unicode-range: U+0020-007F; / Basic Latin only */ }
Optimize Loading Strategy:
-
Critical resources first (async/defer non-critical)
-
Preload critical assets
-
Prefetch likely next pages
-
Service worker for offline/caching
-
HTTP/2 or HTTP/3 for multiplexing
Rendering Performance
Avoid Layout Thrashing:
// ❌ Bad: Alternating reads and writes (causes reflows) elements.forEach(el => { const height = el.offsetHeight; // Read (forces layout) el.style.height = height * 2; // Write });
// ✅ Good: Batch reads, then batch writes const heights = elements.map(el => el.offsetHeight); // All reads elements.forEach((el, i) => { el.style.height = heights[i] * 2; // All writes });
Optimize Rendering:
-
Use CSS contain property for independent regions
-
Minimize DOM depth (flatter is faster)
-
Reduce DOM size (fewer elements)
-
Use content-visibility: auto for long lists
-
Virtual scrolling for very long lists (react-window, react-virtualized)
Reduce Paint & Composite:
-
Use transform and opacity for animations (GPU-accelerated)
-
Avoid animating layout properties (width, height, top, left)
-
Use will-change sparingly for known expensive operations
-
Minimize paint areas (smaller is faster)
Animation Performance
GPU Acceleration:
/* ✅ GPU-accelerated (fast) */ .animated { transform: translateX(100px); opacity: 0.5; }
/* ❌ CPU-bound (slow) */ .animated { left: 100px; width: 300px; }
Smooth 60fps:
-
Target 16ms per frame (60fps)
-
Use requestAnimationFrame for JS animations
-
Debounce/throttle scroll handlers
-
Use CSS animations when possible
-
Avoid long-running JavaScript during animations
Intersection Observer:
// Efficiently detect when elements enter viewport const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { // Element is visible, lazy load or animate } }); });
React/Framework Optimization
React-specific:
-
Use memo() for expensive components
-
useMemo() and useCallback() for expensive computations
-
Virtualize long lists
-
Code split routes
-
Avoid inline function creation in render
-
Use React DevTools Profiler
Framework-agnostic:
-
Minimize re-renders
-
Debounce expensive operations
-
Memoize computed values
-
Lazy load routes and components
Network Optimization
Reduce Requests:
-
Combine small files
-
Use SVG sprites for icons
-
Inline small critical assets
-
Remove unused third-party scripts
Optimize APIs:
-
Use pagination (don't load everything)
-
GraphQL to request only needed fields
-
Response compression (gzip, brotli)
-
HTTP caching headers
-
CDN for static assets
Optimize for Slow Connections:
-
Adaptive loading based on connection (navigator.connection)
-
Optimistic UI updates
-
Request prioritization
-
Progressive enhancement
Core Web Vitals Optimization
Largest Contentful Paint (LCP < 2.5s)
-
Optimize hero images
-
Inline critical CSS
-
Preload key resources
-
Use CDN
-
Server-side rendering
First Input Delay (FID < 100ms) / INP (< 200ms)
-
Break up long tasks
-
Defer non-critical JavaScript
-
Use web workers for heavy computation
-
Reduce JavaScript execution time
Cumulative Layout Shift (CLS < 0.1)
-
Set dimensions on images and videos
-
Don't inject content above existing content
-
Use aspect-ratio CSS property
-
Reserve space for ads/embeds
-
Avoid animations that cause layout shifts
/* Reserve space for image */ .image-container { aspect-ratio: 16 / 9; }
Performance Monitoring
Tools to use:
-
Chrome DevTools (Lighthouse, Performance panel)
-
WebPageTest
-
Core Web Vitals (Chrome UX Report)
-
Bundle analyzers (webpack-bundle-analyzer)
-
Performance monitoring (Sentry, DataDog, New Relic)
Key metrics:
-
LCP, FID/INP, CLS (Core Web Vitals)
-
Time to Interactive (TTI)
-
First Contentful Paint (FCP)
-
Total Blocking Time (TBT)
-
Bundle size
-
Request count
IMPORTANT: Measure on real devices with real network conditions. Desktop Chrome with fast connection isn't representative.
NEVER:
-
Optimize without measuring (premature optimization)
-
Sacrifice accessibility for performance
-
Break functionality while optimizing
-
Use will-change everywhere (creates new layers, uses memory)
-
Lazy load above-fold content
-
Optimize micro-optimizations while ignoring major issues (optimize the biggest bottleneck first)
-
Forget about mobile performance (often slower devices, slower connections)
Verify Improvements
Test that optimizations worked:
-
Before/after metrics: Compare Lighthouse scores
-
Real user monitoring: Track improvements for real users
-
Different devices: Test on low-end Android, not just flagship iPhone
-
Slow connections: Throttle to 3G, test experience
-
No regressions: Ensure functionality still works
-
User perception: Does it feel faster?
Remember: Performance is a feature. Fast experiences feel more responsive, more polished, more professional. Optimize systematically, measure ruthlessly, and prioritize user-perceived performance.