Expensive calculations in React run during every render, slowing the interface. React re-runs a component's body each time its state or props change, so avoiding that repeated work is the core of render performance.
The body runs even when the calculation's inputs are unchanged. The fix is to move the work out, or to skip it when its inputs are stable.
Measure before changing anything
Not every calculation is expensive. A filter over a dozen items costs nothing, while a loop over a hundred thousand rows can dominate a frame.
Wrap a suspect calculation in console.time and console.timeEnd, run the interaction, and check the logged time. Or record the interaction in the Profiler and look for the component with the largest render duration.
Measure in a production build on a device similar to your users, because development mode and Strict Mode inflate render times. A millisecond or more per update is a reasonable signal that the work is worth moving. A chart that recomputes a thousand data points on every keystroke is a candidate, while a label lookup is not.
Move static work out of the component
If a calculation does not depend on props or state, it does not belong inside the component. Move it to module scope so it runs once when the module loads.
const CATEGORY_LABELS = {
tech: "Technology",
design: "Design",
business: "Business",
};
export default function ArticleCard({ category, title }) {
return (
<article>
<span>{CATEGORY_LABELS[category]}</span>
<h3>{title}</h3>
</article>
);
}The lookup table is created once, outside the component. Every render of ArticleCard just reads a value from it instead of rebuilding the map.
The same rule applies to helpers that do not read props or state, such as formatting utilities or a static list of options. Hoisting them removes the cost entirely.
Compute derived values during render
When a value can be calculated from state or props, calculate it directly in the render body. Do not mirror it in state and keep it updated with an Effect.
import { useState } from "react";
export default function Cart() {
const [items, setItems] = useState([]);
const count = items.length;
return <p>{count} items</p>;
}The count is derived from items, so it is computed on every render for free. The alternative, storing count in state and updating it in an Effect, adds a second render and can drift out of sync.
A second render is exactly what you are trying to avoid. Deriving during render keeps one source of truth and removes the Effect entirely.
Memoize only measured work
When a calculation depends on props or state and is genuinely slow, cache it with useMemo so it only recomputes when its inputs change.
import { useMemo } from "react";
export default function Report({ transactions }) {
const totals = useMemo(
() => computeTotals(transactions),
[transactions]
);
return <Summary totals={totals} />;
}computeTotals loops over many transactions. With useMemo, the totals keep the same reference until transactions changes, so Summary can skip work too. The full difference between this and caching a function is covered in useMemo vs useCallback.
Only add this after measuring. useMemo has a small cost of its own, and it does not make the first render faster, only later updates.
Do work in event handlers when you can
A calculation that is triggered by a click does not need to run during render. Do it inside the handler, then store only the result in state. Rendering stays pure, and the work happens once per interaction instead of on every render.
Imagine a CSV export. Clicking Export builds the file in the handler and sets a ready state, so the expensive string generation never touches a render and the list stays responsive.
Let React Compiler do the work
React Compiler memoizes calculations inside components and hooks automatically at build time, so in many codebases you can skip the manual useMemo calls entirely. It is stable and optional, so you can adopt it incrementally.
When it is enabled, the compiler handles memoization for you. Measure either way, so you only change the part that is slow.
Rune AI
Key Insights
- A component body runs on every render.
- Move static work to module scope so it runs once.
- Compute derived values during render instead of storing them.
- Use useMemo only for measured, genuinely slow calculations.
- React Compiler can memoize this work automatically.
Frequently Asked Questions
Should I wrap every calculation in useMemo?
Can I use an Effect to keep a derived value in state?
Conclusion
Avoiding expensive render work starts with measuring, then moving work out of the component, deriving values during render, and memoizing only the calculations the Profiler proves are slow.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.