How do you debug performance issues in React
Start with RUM (web-vitals: LCP, INP, CLS, p75 in prod). Reproduce in dev → React DevTools Profiler shows which components rendered, why, and how long. Chrome Performance panel for main-thread tasks. Lighthouse for asset/CSS issues. Memory panel for leaks. Bundle analyzer for size. Don't optimize without measurement.
Step 1 — measure prod
You can't optimize what you don't measure. RUM in production beats lab in dev:
import { onLCP, onINP, onCLS } from "web-vitals";
onLCP(({ value }) => beacon({ name: "LCP", value }));
onINP(({ value }) => beacon({ name: "INP", value }));
onCLS(({ value }) => beacon({ name: "CLS", value }));Track p75 per route. Set budgets (LCP < 2.5s, INP < 200ms, CLS < 0.1). Regression alerts.
Step 2 — reproduce
Get a reliable local repro. CPU throttle (4x), network throttle (Slow 4G). What feels jank-free on a M1 MacBook can be unusable on a low-end Android.
Step 3 — React DevTools Profiler
- Record an interaction.
- Flamegraph shows which components rendered and how long.
- Ranked shows expensive renders sorted.
- "Why did this render?" hover tooltip → state/props/parent.
- Highlight Updates setting in DevTools shows visually which nodes re-render.
What to look for:
- Unexpected re-renders (parent state change cascading).
- A single slow component (heavy compute, big list).
- Context fan-out (every consumer re-renders).
Step 4 — Chrome Performance panel
- Record a slow interaction.
- Look at the main thread: long tasks (> 50ms) flagged.
- Identify scripts / handlers eating time.
- Bottom-up view shows where ms went.
INP regressions are usually visible here as a long task in an event handler.
Step 5 — Lighthouse
- LCP element flagged.
- Render-blocking resources.
- Unused CSS / JS.
- Image opportunities.
Great for the "initial load" category. Less useful for runtime / INP.
Step 6 — Bundle analyzer
ANALYZE=true next build
# or webpack-bundle-analyzer / source-map-explorer / vite-plugin-visualizerIdentify the heaviest dependencies. Common offenders: moment (use date-fns), lodash full (use lodash-es per-import or just JS), big icon sets imported unsplit.
Step 7 — Memory panel
For leaks: heap snapshot, do the suspected leaky thing N times, heap snapshot again, compare. Detached DOM report finds zombie nodes.
Step 8 — Targeted fixes by category
| Symptom | Fix |
|---|---|
| Slow LCP | Hero image optimization, RSC/SSR, fewer render-blocking resources. |
| High INP | Break long tasks, transitions, Workers, debounce. |
| Many re-renders | Split context, memoize stable callbacks, fix unstable prop refs. |
| Long list jank | Virtualize. |
| Slow filter/sort | useMemo, useDeferredValue, Web Worker. |
| Bundle bloat | Code splitting, dynamic imports, tree-shake, swap heavy deps. |
| Memory growth | Cleanup in effects, AbortController, bounded caches. |
Anti-patterns
- Sprinkling useMemo without profiling.
- Optimizing the first thing you see in DevTools without confirming it's the bottleneck.
- Trusting lab-only metrics on a dev machine.
- Ignoring p99 / mobile because p50 looks fine.
Workflow I follow
- Get RUM data.
- Identify worst route + metric.
- Reproduce with CPU/network throttling.
- Profile in DevTools.
- Hypothesize fix.
- Implement.
- Measure delta.
- Ship.
- Watch prod RUM.
Interview framing
"Start with RUM (web-vitals p75 in prod) — without it you're guessing. Then reproduce with CPU + network throttling. React DevTools Profiler shows render flamegraphs and 'why did this render'; highlights unexpected re-renders. Chrome Performance panel for main-thread long tasks (INP). Lighthouse for initial load issues. Memory panel for leaks. Bundle analyzer for size. Targeted fix by category — context fan-out, unmemoized callbacks, long lists, big bundle, etc. Always measure delta before/after; many memoization PRs don't actually move metrics. And optimize for mobile / low-end, not the M1 MacBook."
Follow-up questions
- •What's your default profiling toolkit?
- •Walk through a recent perf debug.
- •How do you decide where to start?
Common mistakes
- •Optimizing without measuring.
- •Trusting dev machine metrics.
- •Stopping at the first 'fix' without delta measurement.
Performance considerations
- •The whole topic.
Edge cases
- •Profiler overhead affects measurements.
- •Dev vs prod build differences.
- •Strict Mode double-mount in dev.
Real-world examples
- •Vercel's perf insights, web.dev case studies, React docs profiling guide.