WEB ATELIER (UDIT) Β· Learning by doing, with theory, practice and shared reflection

Performance: Speed as a Feature

Draft

URL: https://ruvebal.github.io/web-atelier-udit/lessons/en/react/react-performance/

πŸ“‹ Table of Contents

β€œPerformance is not an optimization. It is a feature that users feel.”


🎯 Sprint Goal

By the end of this sprint: Your app is fastβ€”both in perceived performance (what users feel) and actual performance (what Lighthouse measures). You’ll profile, optimize, and prove the difference.


πŸ“ Position in Journey

Sprint Focus Your App Grows
10. Auth Security User sessions
11. Testing Quality Reliable codebase
β†’ 12. Performance Speed Optimized experience
13. Deployment Launch Live on the web

🧭 Learning Objectives

By the end of this lesson, you will:

  • Use React DevTools Profiler to find slow renders
  • Apply React.memo, useMemo, and useCallback correctly
  • Implement code splitting with lazy() and Suspense
  • Optimize images and assets
  • Measure Core Web Vitals and Lighthouse score
  • Understand when NOT to optimize

πŸ—οΈ Performance Checklist

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              PERFORMANCE PRIORITY                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                          β”‚
β”‚  1. PERCEIVED PERFORMANCE (What users feel)              β”‚
β”‚     β€’ Skeleton loading states                            β”‚
β”‚     β€’ Optimistic updates                                 β”‚
β”‚     β€’ Immediate visual feedback                          β”‚
β”‚                                                          β”‚
β”‚  2. BUNDLE SIZE (What ships to browser)                  β”‚
β”‚     β€’ Code splitting by route                            β”‚
β”‚     β€’ Tree shaking (only import what you use)            β”‚
β”‚     β€’ Analyze with bundle analyzer                       β”‚
β”‚                                                          β”‚
β”‚  3. RUNTIME PERFORMANCE (How fast it runs)               β”‚
β”‚     β€’ Minimize unnecessary re-renders                    β”‚
β”‚     β€’ Virtualize long lists                              β”‚
β”‚     β€’ Debounce expensive operations                      β”‚
β”‚                                                          β”‚
β”‚  4. ASSETS (Images, fonts, etc.)                         β”‚
β”‚     β€’ Lazy load images                                   β”‚
β”‚     β€’ Use modern formats (WebP, AVIF)                    β”‚
β”‚     β€’ Optimize font loading                              β”‚
β”‚                                                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ”§ Optimization Techniques

Code Splitting

// Before: Everything in one bundle
import { Dashboard } from './pages/Dashboard';
import { Settings } from './pages/Settings';

// After: Load on demand
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));

// Wrap with Suspense
<Suspense fallback={<PageSkeleton />}>
  <Routes>
    <Route path="/dashboard" element={<Dashboard />} />
    <Route path="/settings" element={<Settings />} />
  </Routes>
</Suspense>

Memoization (Use Sparingly)

// Only memoize when you've PROVEN a performance problem

// For expensive calculations
const sortedItems = useMemo(
  () => items.sort((a, b) => a.price - b.price),
  [items]
);

// For callback stability (when passed to memoized children)
const handleClick = useCallback(
  () => setCount(c => c + 1),
  []
);

// For component re-renders
const MemoizedChild = React.memo(({ data }) => (
  <ExpensiveComponent data={data} />
));

πŸŽ“ Methodology: Atelier Practice

The Sprint Rhythm

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ DAY 1: Measure First                                    β”‚
β”‚   β€’ Run Lighthouse, record baseline scores              β”‚
β”‚   β€’ Profile with React DevTools                         β”‚
β”‚   β€’ Identify top 3 performance issues                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ DAY 2: Optimize                                         β”‚
β”‚   β€’ Implement code splitting for routes                 β”‚
β”‚   β€’ Add lazy loading for images                         β”‚
β”‚   β€’ Apply memoization to proven bottlenecks             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ DAY 3: Verify & Document                                β”‚
β”‚   β€’ Re-run Lighthouse, compare before/after             β”‚
β”‚   β€’ Document optimizations in README                    β”‚
β”‚   β€’ Present: What worked? What was premature?           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The Golden Rule

β€œMeasure, then optimize. Never optimize based on intuition alone.”


AI-Assisted Development Protocol

Task AI Role Your Role
Analyze Lighthouse report Explain each metric Prioritize fixes
Suggest optimizations Propose techniques Measure before/after
Debug performance issues Explain React render cycle Apply correct solution
Generate lazy imports Scaffold code splitting Test loading states

πŸ“ Sprint Deliverables

  • Baseline Lighthouse score documented
  • Code splitting for at least 3 routes
  • Image optimization (lazy loading, proper sizing)
  • Memoization applied to 1-2 proven bottlenecks
  • Final Lighthouse score (aim for 90+ performance)
  • Before/after comparison in README
  • Reflection: What was the biggest win? What was premature?

πŸ“Š Core Web Vitals

Metric What It Measures Target
LCP (Largest Contentful Paint) Loading speed < 2.5s
FID (First Input Delay) Interactivity < 100ms
CLS (Cumulative Layout Shift) Visual stability < 0.1

πŸ”— Lesson Navigation

Previous Current Next
Testing Performance Deployment

πŸ“š Key Concepts Preview

Performance is a UX contract

  • Users feel latency as β€œbrokenness”.
  • Performance work must be measured (profile first, optimize second).
  • Prefer reducing work (less rendering, less JS, fewer requests) over micro-optimizations.

Three practical levers

1) Rendering: avoid unnecessary re-renders (stable props, component boundaries). 2) Loading: split bundles, defer non-critical code, optimize images. 3) Data: cache server state, avoid refetch storms, handle race conditions.

Example: β€œdon’t memoize blindly”

// βœ… Memoize ONLY when you measured re-render cost.
const expensive = useMemo(() => compute(data), [data]);

// ❌ This often makes code harder without measurable benefit:
const onClick = useCallback(() => setOpen(true), []);

Reflection (Atelier)

πŸ’­ Which metric did you improve? What evidence do you have (before/after)?

πŸ’­ What β€œoptimization” made your code worse? Why?

Koan

β€œThe profiler is the mirror. Without it, you polish shadows.”


β€œThe fastest code is the code that doesn’t run. The fastest component is the one that doesn’t re-render.”