Back to React
React
medium
mid

How would you optimize performance in a React app with large component trees?

Profile first. Then: split context so updates fan out narrowly, memoize expensive subtrees with stable prop refs, virtualize lists, lift hot state out of Context to a selector store, use `useDeferredValue` / `startTransition` for non-urgent updates, RSC for non-interactive parts, code split routes. Avoid the 'memoize everything' anti-pattern — most renders are cheap.

4 min read·~10 min to think through

Large trees are not inherently slow — React reconciles fast. They become slow when state changes cause re-renders to cascade into expensive subtrees.

Step 1 — profile

React DevTools Profiler:

  • Record a slow interaction.
  • Look at the flamegraph — long bars = expensive renders.
  • "Why did this render" → unexpected re-renders.
  • Highlight Updates renders to visualize cascades.

Step 2 — diagnose by category

Cascade from parent state

Symptom: typing in a field re-renders the whole page. Fix: localize the field's state (it doesn't need to live at the root).

Context fan-out

Symptom: any state change re-renders every consumer. Fix: split context; move hot state out to a selector-based store (Zustand, Redux with useSelector).

Unstable prop references

Symptom: memo'd child re-renders even when data hasn't changed. Fix: useMemo / useCallback in the parent for the props passed to memo'd children.

Long lists

Symptom: 5k+ DOM nodes; scroll jank. Fix: virtualize.

Expensive derivations on every render

Symptom: filter/sort/map computed each render. Fix: useMemo with proper deps, or move to a Worker if heavy.

Step 3 — fix selectively

tsx
const Row = React.memo(({ item }) => /* ... */);    // for genuinely heavy rows
const callback = useCallback((id) => ..., []);     // stable ref for memo'd child

Only on the actual hot path. Memo on everything = wasted comparison cost.

Step 4 — deferred + transitions

tsx
const deferred = useDeferredValue(query);          // expensive computation lags
startTransition(() => setLargeFilteredList(...));  // input stays responsive

Step 5 — structural changes

  • RSC for non-interactive parts → less client JS.
  • Code split per route / heavy feature → less initial bundle.
  • Virtualize long lists.
  • Move heavy compute to Workers (parse, search index, image processing).

Step 6 — measure delta

Re-profile. If the metric didn't improve, revert and look elsewhere.

Anti-patterns

  • Sprinkling memo / useCallback before profiling.
  • Wrapping cheap components in React.memo (the comparison cost adds up).
  • Returning new object literals as memoized callback deps.
  • "Optimizing" a 50-node tree.

Interview framing

"Profile first — React DevTools Profiler tells you which components rendered, why, and how long. Most renders are cheap; problems come from cascades into expensive subtrees. Diagnose by category: parent state cascading (localize), Context fan-out (split context, move hot state to a selector store), unstable prop refs defeating memo (stable refs via useMemo/useCallback in parents), long lists (virtualize), expensive derivations (useMemo or Worker). Then deferred updates / transitions for non-urgent work, RSC for non-interactive parts, code split per route. Measure delta. Don't memoize everything — comparison cost outweighs savings on cheap components."

Follow-up questions

  • When does memoization hurt?
  • How do you fix Context fan-out?
  • What's the biggest perf win you've shipped?

Common mistakes

  • Memoizing without profiling.
  • Context with frequent updates.
  • Ignoring structural fixes (virtualization, RSC).

Performance considerations

  • Structural fixes outperform micro-memo. Profile, fix, measure.

Edge cases

  • Strict Mode double-rendering in dev distorts numbers.
  • Production profiling differs from dev.
  • Memo + new prop refs.

Real-world examples

  • Slack desktop, Linear web, large dashboards.

Senior engineer discussion

Seniors take a measurement-first stance and reach for structural changes before micro-optimizations.

Related questions