Skip to content

Performance Optimization

advanced2 min read
  • React

Keeping a React app responsive by measuring first, then reducing unnecessary re-renders and expensive work with tools like memoization, applied deliberately.

Prerequisites

  • Hooks and how state triggers re-renders (see Related Topics)

What you'll understand

  • Why measuring must come before optimizing
  • The main causes of slowness in a React app
  • How memoization reduces unnecessary work

Explanation

Performance optimization keeps an app feeling fast, but its first rule is to measure before you change anything. Most code is not the bottleneck, and optimizing by guesswork adds complexity while fixing nothing. Use the browser and React profiling tools to find what is actually slow, then optimize that. Premature optimization is a real cost.

In React, the usual culprits are unnecessary re-renders and expensive work repeated on every render. When state changes, a component and its children re-render; if that happens too often, or a child does heavy work each time, the UI can stutter. The tools to address this are memoization: memo skips re-rendering a component when its props have not changed, useMemo caches the result of an expensive calculation, and useCallback keeps a function reference stable between renders.

Memoization is not free, though, it adds code and its own small overhead, so apply it where profiling shows a genuine problem, not everywhere by default. Often the bigger wins come from elsewhere: sending less code to the browser through code splitting and loading parts of the app lazily (see Related Topics), avoiding oversized lists, and not putting rapidly changing values into a widely shared context. Optimize deliberately, guided by measurement.

Examples

useMemo caches an expensive calculation so it only re-runs when its inputs change:

const sorted = useMemo(
  () => hugeList.slice().sort(compare),
  [hugeList],
); // recomputed only when hugeList changes

memo skips re-rendering a component when its props are unchanged:

const Row = memo(function Row({ item }: { item: Item }) {
  return <li>{item.name}</li>;
});

Common mistakes

  • Optimizing before measuring, adding complexity that fixes nothing.
  • Wrapping everything in memo, useMemo, and useCallback by reflex.
  • Putting frequently changing values in a broad context, re-rendering many components.
  • Ignoring bundle size while micro-optimizing render time.

Best practices

  • Profile first and optimize the parts that are genuinely slow.
  • Use memoization deliberately where it addresses a measured problem.
  • Reduce what ships to the browser with code splitting and lazy loading (see Related Topics).
  • Prefer structural fixes (less work, smaller lists) over scattered micro-optimizations.

Further reading

Related topics