Explore advanced React optimisation techniques including React.memo, useMemo, useCallback, react-window virtual lists, and Profiler-driven debugging.

Why React Performance Matters

Large React applications accumulate unnecessary re-renders over time. A 200 ms interaction delay feels instant on localhost but catastrophic in production, especially on mid-range mobile devices.

The Golden Rule

Measure first. Optimise second. Open React DevTools Profiler and record a slow interaction. Look for components with long render times or high commit count before reaching for memo.

1. React.memo — Shallow Prop Equality

tsx
const Row = React.memo(({ item }: { item: Item }) => (
  <div className="row">{item.name}</div>
));

React.memo prevents re-render when props are shallowly equal. It has a small overhead — only apply it to components that render frequently with stable props.

2. useMemo — Expensive Computations

tsx
const sorted = useMemo(
  () => [...items].sort((a, b) => a.score - b.score),
  [items]
);

Use useMemo for transformations that are genuinely expensive (>1 ms). Avoid wrapping cheap calculations — the dependency comparison cost can exceed the computation cost.

3. useCallback — Stable Function References

tsx
const handleDelete = useCallback((id: string) => {
  dispatch({ type: 'DELETE', id });
}, [dispatch]);

Stable references prevent child memos from invalidating. Most useful when passing callbacks to memoised children or effect dependency arrays.

4. Virtual Lists with react-window

Render only visible rows for lists with 500+ items:

tsx
import { FixedSizeList as List } from 'react-window';

<List height={600} itemCount={items.length} itemSize={50} width="100%">
  {({ index, style }) => <Row style={style} item={items[index]} />}
</List>

5. Code Splitting with next/dynamic

tsx
const HeavyChart = dynamic(() => import('@/components/HeavyChart'), {
  loading: () => <Skeleton />,
});

Route-level splitting happens automatically with the App Router. Component-level splitting is for client-heavy components loaded conditionally.

Summary Table

TechniqueWhen to UseReact.memoFrequently rendered leaf componentsuseMemoExpensive transforms (sort, filter, derive)useCallbackStable callback refs for memoised childrenreact-windowLists > 500 itemsdynamic()Heavy components loaded conditionally