Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

To optimize a slow React interaction, profile that interaction first, identify the component or calculation doing unnecessary work, then change only what the measurements justify. Start with React Developer Tools Profiler; use structural fixes before adding memoization, and treat memo, useMemo, and useCallback as performance tools—not correctness requirements. For new code, React Compiler can automate much of the memoization work.

How to find the work that is actually slowing down

  1. Reproduce one slow interaction. Choose a specific action, such as typing into a search field or opening a panel, and use the same steps each time.
  2. Record it with React Developer Tools Profiler. Inspect which components rendered during the interaction and where rendering time was spent. React recommends profiling to identify candidates for memoization: React’s memo reference.
  3. Trace the likely cause. Check whether the cost comes from a component rendering repeatedly, an expensive calculation, changing props, or an update chain triggered by state or an Effect.
  4. Make one focused change and profile again. Compare the same interaction in a comparable environment. Keep the change only if it improves the observed problem without introducing incorrect behavior.

Profiler timings describe your application and the update you recorded; they are not universal benchmarks. Development behavior can distort timings, and profiling itself adds overhead. For production-oriented measurements, use a production-like environment; the React <Profiler> API requires a special profiling build because it is disabled in an ordinary production build. See React’s <Profiler> reference.

When memo can avoid a component render

memo(Component) returns a component that will usually skip rendering when its props compare equal to the previous props. It is most useful when a component renders often with the same props and its render logic is expensive. React compares props using shallow equality by default, so a new object or function prop counts as changed even when its contents or behavior appear equivalent. The official guidance is explicit: “memoization is a performance optimization, not a guarantee.” Read React’s memo reference.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Stable props make memoization more likely to help

In this example, the parent creates a new object each time it renders, so the memoized child receives a changed prop:

const Results = memo(function Results({ options }) {
  return <ExpensiveList options={options} />;
});

function SearchPage() {
  const [query, setQuery] = useState('');
  return <Results options={{ query, limit: 20 }} />;
}

When profiling shows this identity change is preventing a useful skip, a cached value can provide a stable object while its inputs remain unchanged:

function SearchPage() {
  const [query, setQuery] = useState('');
  const options = useMemo(() => ({ query, limit: 20 }), [query]);
  return <Results options={options} />;
}

This is worthwhile only if avoiding the child’s work matters in the measured interaction. Adding memoization to every component can add complexity without a user-visible benefit.

What memo does not prevent

Memoization does not block a component from rendering when its own state changes or when context it reads changes. Nor does it guarantee that React will never render the component. Avoid relying on a skipped render for correctness; rendering must produce the right result whether or not a performance optimization applies.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When to use useMemo or useCallback

useMemo caches the result of a pure calculation between renders when every listed dependency remains equal under Object.is. It does not make the initial calculation faster. Consider it when profiling shows a calculation is noticeably slow, when a stable value enables a memoized child to skip work, or when a value’s changing identity triggers unnecessary work in another Hook. See React’s useMemo reference.

useCallback is the corresponding tool for retaining a function identity while its dependencies remain unchanged. It is useful when a stable callback enables a memoized child to skip a render or prevents unnecessary work tied to a Hook dependency. Wrapping a function in useCallback does not stabilize a separate inline function created and passed elsewhere; the identity that matters must be the one actually passed to the consumer.

Keep dependencies complete

List every reactive value used by the calculation or callback. Omitting a dependency can leave the cached result or function using stale values, causing a correctness bug rather than a performance improvement. Keep React’s lint rules enabled to catch missing dependencies. The cache is an optimization, not a semantic guarantee: React may discard it in documented circumstances, so application behavior must not depend on the cache existing.

Should you add useMemo everywhere?

No. A cache has a maintenance cost, and it cannot help an expensive calculation on its first render. First check whether the work is actually slow and whether it repeats in the interaction you care about. If the calculation is cheap or its inputs change each time, memoization may add code without avoiding meaningful work.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Try structural fixes before adding more Hooks

Some performance problems come from how updates are organized, not from a missing memoization Hook. These changes can prevent unnecessary work at its source:

  • Keep transient state close to where it is used. If a state change only affects one section, avoid placing that state so high in the tree that it forces unrelated parts to update.
  • Pass JSX as children to wrapper components where appropriate. A wrapper that updates its own state can often accept its surrounding content as children, allowing that content to remain unchanged rather than being recreated by the wrapper.
  • Keep rendering pure. A component should calculate its output from props, state, and context rather than causing side effects during rendering. Pure render logic is easier for React to optimize predictably.
  • Remove unnecessary Effect-driven state updates. An Effect that updates state can trigger another render and create an avoidable update chain. Remove unnecessary Effects or dependencies where the behavior does not require them.

These approaches are described in React’s guidance on memo and useMemo.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How React Compiler changes the optimization workflow

React Compiler analyzes React data flow at build time and automatically memoizes components and values. React’s guidance is to rely on compiler memoization for new code, reducing the need to add manual memoization routinely. Compiler adoption still requires a compatible setup and checks for your codebase; see React Compiler and its introduction.

The compiler does not tell you which user interaction is slow or prove that a change improves it. Continue to profile the symptom and validate the result. For existing code, do not remove manual memoization wholesale just because the compiler is enabled; preserve it unless you have carefully tested that removing it is safe and does not worsen the behavior you measured. React also documents compiler directives at React Compiler directives.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choosing an optimization by the work it avoids

Approach Work it can avoid When it applies What to verify
memo Rendering a component Props compare equal and the component’s render work is worth skipping Profile whether the component renders during the target interaction and whether stable props let it skip
useMemo Repeating a calculation or recreating a value Dependencies are unchanged, and caching the calculation or value identity avoids useful work Measure the calculation or the downstream work that depends on the value
useCallback Changing a function identity A stable callback enables a memoized consumer or avoids dependency-driven work Confirm the consumer receives this stable callback and that its dependencies are complete
React Compiler Compiler-inferred component and value work The compiler is correctly set up and can analyze the relevant code Test the build and profile the same interaction; retain safe behavior in existing code
Structural changes Unrelated renders or avoidable update chains State, wrapper composition, render logic, or Effects are causing unnecessary updates Check that the update path is simpler and that the target interaction improves

A final measurement checklist

  • Did you reproduce the same user interaction before and after?
  • Did you use a comparable environment and account for development-mode behavior?
  • Did you isolate the component, calculation, identity change, or update chain responsible for the cost?
  • Does the change preserve correct output when memoization is not applied?
  • Did the profiler show an improvement in the target interaction, rather than merely a more complicated component?

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.