Performance: Speed as a Feature
DraftURL: https://ruvebal.github.io/web-atelier-udit/lessons/en/react/react-performance/
π Table of Contents
- π― Sprint Goal
- π Position in Journey
- π§ Learning Objectives
- ποΈ Performance Checklist
- π§ Optimization Techniques
- π Methodology: Atelier Practice
- π Sprint Deliverables
- π Core Web Vitals
- π Lesson Navigation
- π Key Concepts Preview
β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, anduseCallbackcorrectly - Implement code splitting with
lazy()andSuspense - 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.β