"Make it faster" is the vaguest ticket in frontend. Faster what? Measured how? Most React perf advice is a pile of useMemo everywhere and a vague feeling of virtue — with no numbers to prove it helped (and sometimes it made things worse).
So I took a real production-shaped app — a SaaS analytics dashboard with a 12,000-row table, live filters, and charts — profiled its actual bottlenecks, and fixed them one technique at a time, measuring before/after each. This is the condensed version; the full guide (every technique with before/after code, diagrams, and the full metrics matrix) is on my site 👇
Full guide: https://prepstack.co.in/blog/react-performance-optimization-techniques-before-after-metrics-guide
The result (same app, profiled before + after)
| Metric | Baseline | After | Improvement |
|---|---|---|---|
| LCP (load) | 4.1s | 1.3s | 68% faster |
| INP (interactivity) | 420ms | 90ms | 79% faster |
| JS bundle (gzipped) | 880 KB | 240 KB | 73% smaller |
| Filter re-render | 678ms | 18ms | 97% faster |
| Table mount | 1,400ms | 45ms | 97% faster |
| Scroll FPS | 22 | 60 | smooth |
| Memory (heap) | 310 MB | 120 MB | 61% less |
Where the wins actually came from
Virtualization ~35%
Code splitting + bundle ~25%
Server Components / SSR ~18%
Re-render elimination ~12%
Concurrent features ~6%
Smaller wins ~4%
The top three structural techniques delivered ~78% of the total gains. Scattering useMemo everywhere — what most perf posts lead with — was the smallest bucket.
The techniques, briefly
1. Virtualization — the biggest single win. The user sees ~30 rows; rendering the other 11,970 is waste. @tanstack/react-virtual took DOM nodes 96,000 → ~320. Virtualize any list over ~200 rows.
2. Code-splitting + bundle trimming. React.lazy + Suspense for the heavy export/settings modals (most users never open them), plus moment→date-fns and lodash→lodash/x. Bundle 880KB → 240KB, parse 2.3s → 0.7s.
3. Server Components / SSR / streaming. Fetch on the server, stream the shell instantly. Time-to-first-content 1.1s → 0.2s, client JS ~60% less, fetch waterfall eliminated.
4. Re-render elimination. memo + stable useCallback/useMemo stopped the chart panel re-rendering on every keystroke. But memo isn't free — apply where the Profiler shows a real repeated expensive re-render, not everywhere.
5. Concurrent features. useTransition / useDeferredValue keep the input snappy while filtering — INP 180ms → 90ms, zero dropped frames.
6. React 19 Compiler. Auto-memoizes for you, so most manual useMemo/useCallback becomes noise you can delete.
The mental model
React performance isn't "add useMemo and hope." It's a loop: profile → find the real bottleneck → apply the right technique → re-measure → keep it only if the number moved. Reach for structural wins (virtualize, split, server-render) first — they deliver 10× what scattered memoization does.
The full guide has every technique with before/after code, the diagrams, the complete metrics matrix, and the decision flow:
https://prepstack.co.in/blog/react-performance-optimization-techniques-before-after-metrics-guide
Originally published on PrepStack.
Top comments (0)