React Render Performance

React render performance is about keeping updates responsive by measuring component work and reducing unnecessary or expensive renders. Start with profiling, then improve state boundaries, computation, or memoization where the measurements show a real cost.

A React update can cause a component to render again when its state changes, when its parent renders, or when its context value changes. A render means React calls the component to calculate the next UI; it does not automatically mean the browser performs a costly DOM update.

The first step is measurement. Use the React DevTools Profiler to identify slow interactions, frequently rendering components, and components whose render work is unexpectedly expensive. Avoid optimizing components merely because they render often; frequent renders can be harmless when the work is small.

A common improvement is to place state near the components that actually use it. If rapidly changing state lives high in the tree, many descendants may participate in updates unnecessarily. Splitting components can also isolate expensive work from unrelated state changes.

Memoization can reduce repeated work when its cost is justified. `React.memo` can skip a child render when its props are unchanged, while `useMemo` can cache a calculated value and `useCallback` can preserve a function reference between renders. These tools add complexity and comparisons, so they should support a measured performance problem rather than replace profiling.

Expensive calculations can also be simplified or moved out of the render path when appropriate. For interactions involving large lists, techniques such as virtualization can reduce the amount of UI work needed at once. The goal is not to eliminate renders, but to keep the work caused by important interactions small enough to remain responsive.

Key points

Interview questions

What causes a React component to render again?

A component can render again when its own state changes or when its parent renders. Context updates can also cause consumers to render when the relevant context value changes. Props changing often matter because they are commonly produced by parent renders, but receiving the same primitive prop does not by itself force a component to render independently of its parent. The important distinction is that rendering means recalculating the component output, not necessarily changing the DOM.

How would you investigate a slow React interaction?

I would first reproduce the specific interaction and measure it with the React DevTools Profiler. I would identify which components render during the interaction and which ones consume substantial render time. Then I would inspect state placement, expensive calculations, prop identity, and context updates to find the source of unnecessary work. After making a targeted change, I would profile again to verify that the interaction improved.

When is `React.memo` useful?

`React.memo` can help when a component renders frequently with the same props and its rendering work is expensive enough to matter. It allows React to reuse the previous result when the component's props compare equal. It is less useful when props change on most renders or when the component's own state or consumed context still causes updates. I would normally use profiling to justify the optimization rather than applying it universally.

What is a good way to reduce unnecessary rendering in a component tree?

I would first identify which state or context update is propagating through the tree. If rapidly changing state is owned by a high-level component, I would consider moving that state closer to the components that need it. I would also split components so expensive subtrees are isolated from unrelated updates. Memoization can then be applied selectively when stable props allow meaningful work to be skipped.

Why shouldn't you optimize every component with `useMemo` and `useCallback`?

Both hooks have their own bookkeeping and dependency management, so using them everywhere can add complexity without producing a meaningful benefit. Many component calculations are already cheap enough that recomputing them is preferable to maintaining a cache. They can be useful when a calculation is expensive or when stable identity is needed for another optimization. The decision should be based on the actual performance characteristics of the application.