October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk8 min

React Performance Patterns for Everyday Development

A practical sequence for React responsiveness: measure the slow interaction, remove avoidable update work, then use useMemo, memo, deferred updates, or lazy loading only where the measurement justifies them.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To improve React responsiveness, find the one interaction that feels slow, measure the components involved, remove update work that should not happen at all, and only then apply the smallest technique that fits what remains. Memoization is a targeted fix for a measured cost. It is not a default wrapper for every component, and React’s own guidance treats it that way.

Start with one slow interaction

Pick a specific interaction, such as typing in a search box, opening a panel, or switching a tab. Optimizing “the app” in general rarely produces a clear target. React’s official guidance recommends the Developer Tools Profiler when a particular interaction stays slow, because it shows which components rendered during that interaction and how long they took.

  1. Open the Profiler in React Developer Tools and start a recording.
  2. Perform the slow interaction once, then stop the recording.
  3. Find the commit that corresponds to the lag and list the components that rendered in it.
  4. Ask two questions of each one: did its output actually change, and is it expensive? Components that re-rendered with identical output and a noticeable render time are the candidates for memoization.

When you need numbers in code rather than in the extension, the Profiler component wraps a section of the tree and calls an onRender callback whenever something inside it commits an update. Two of its timing values matter most. actualDuration is the time spent rendering that update. baseDuration is an estimate of the render cost without any optimizations, which helps you judge how much memoization could plausibly save.

function onRender(id, phase, actualDuration, baseDuration) {
  console.log(id, phase, actualDuration, baseDuration);
}

<Profiler id="ProductList" onRender={onRender}>
  <ProductList items={items} />
</Profiler>

Keep the limits of these measurements in mind:

  • Profiling adds overhead. It is disabled by default in production builds. React documents a profiling-enabled production build for situations where you need production timings.
  • Development timings are not fully representative. Strict Mode can invoke render logic more than once in development, so the numbers can overstate the cost.
  • Test a production build with CPU throttling. Throttling approximates the experience on slower devices, which is where responsiveness problems usually show up.

Do not report or expect a specific speedup until you have measured it in your own app, on your own build, under conditions that match your users.

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

Remove update work that should not happen

Most of the time, the cheapest fix is to stop the extra renders from happening. React’s useMemo documentation puts it directly: “Most performance problems in React apps are caused by chains of updates originating from Effects that cause your components to render over and over.” An Effect that sets state, which triggers another render, which runs another Effect that sets state, produces exactly this pattern.

Three habits prevent most of it:

  • Derive values during render instead of storing them in state. If a value can be computed from props or state, compute it where it is used. Copying it into state through an Effect adds a render and a chance for the copy to fall out of date.
  • Keep transient state close to the components that use it. State held high in the tree re-renders everything beneath it when it changes, even when most of those children do not use it.
  • Keep render logic pure. Render should read props and state and return output. Side effects belong in Effects or event handlers.

Effect dependencies deserve a separate check. If an Effect depends on an object or function created during render, that dependency changes on every render and can retrigger the Effect. Before reaching for memoization to stabilize it, move the object or function inside the Effect, or move it outside the component if it does not depend on props or state.

// Avoid: an Effect copies derived data into state
const [visible, setVisible] = useState([]);
useEffect(() => {
  setVisible(items.filter(item => item.inStock));
}, [items]);

// Prefer: compute it during render
const visible = items.filter(item => item.inStock);

If the derived calculation is itself slow, the question of caching it comes next, and that is where useMemo fits.

Stop unnecessary re-renders with memo

memo wraps a component so React can skip re-rendering it when its props have not changed. By default, React compares each prop with Object.is. This works well for primitives such as strings and numbers, but it has consequences for objects, arrays, and functions:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • A new object, array, or function created on each parent render has a new identity, so the child’s props look changed and memo cannot skip the render.
  • Stabilizing those values with useMemo or useCallback is the usual fix, but only when the child is measured as expensive.
  • A custom comparison function can detect equal props that Object.is misses, but the comparison itself costs time, and it runs on every render of the child.
  • memo does not block updates caused by the component’s own state or by the context it consumes. The component still re-renders in those cases.

React’s memo documentation makes the same point in one line: “memoization is a performance optimization, not a guarantee.” Treat a successful skip as a bonus, and verify it in the Profiler rather than assuming it.

When should I use useMemo?

useMemo(() => calculate(a, b), [a, b]) stores the result of a calculation and returns the stored value on later renders as long as its dependencies remain equal under Object.is. React’s documentation states that it will not discard the cached value unless there is a specific reason to do so.

Two limits matter. First, useMemo does not make the first render faster, because nothing is cached yet. Second, the calculation must be pure, since React may rely on it producing the same result for the same inputs.

The documentation describes two situations where it earns its place: a calculation that is noticeably slow with dependencies that stay relatively stable, and a stable value that helps a memoized child skip work. The table below applies those conditions.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Situation Use useMemo? Reasoning
A pure calculation is measured as slow, and its inputs usually stay the same Yes The cached result skips the calculation on renders where nothing it depends on has changed.
An object or array is passed to a child wrapped in memo Yes, if the child is measured as expensive A stable identity lets the child’s memo comparison succeed.
The calculation is cheap, or not shown to be a bottleneck Usually no There is no measured cost to remove, and the caching adds code to maintain.
The inputs change on almost every render Usually no The cache is rarely reused, so it saves little.
The slow part happens on the first render only No The documentation is explicit that useMemo does not speed up the initial render.

If your project uses React Compiler, which can automatically memoize values, functions, and components, check its output before adding manual annotations. Duplicate manual memoization can make code harder to read without measurable benefit.

Keep typing responsive while filtering a large list

This is the case where the expensive work is necessary, but it should not block the input. A search box that re-renders a 5,000-row list on every keystroke will feel sluggish because the urgent update, the text field, waits for the expensive update, the list. React provides two tools that separate the two, and both prioritize rendering rather than making the filtering itself cheaper.

useDeferredValue for a value you receive

Use useDeferredValue when the expensive part depends on a value you read, and you do not control the code that sets it. The input stays current while the list uses a deferred copy that React can update later.

const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);

// The input uses query; the list uses deferredQuery
<input value={query} onChange={e => setQuery(e.target.value)} />
<ProductList filter={deferredQuery} />

useTransition for an update you own

Use useTransition when you control the state update and can mark the non-urgent part as a transition. The urgent state update applies first, and the transition update follows as React has time to render it.

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

The user-visible tradeoff is the same for both. The deferred section can temporarily show results from the previous input while the urgent input is already updated. Decide whether that brief lag is acceptable for the content, and make sure the interface does not present stale results as current.

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

Load rarely used code with lazy and Suspense

lazy defers loading a component’s code until it is first rendered. Wrap the import, then place the component under a Suspense boundary so React can show fallback content while the code loads.

const Chart = lazy(() => import('./Chart'));

<Suspense fallback={<p>Loading chart...</p>}>
  <Chart data={data} />
</Suspense>

Placement determines the experience. A boundary high in the tree can hide a large part of the page behind one fallback, while many small boundaries can produce a series of loading indicators that flicker in sequence. Choose boundaries that match how a user moves through the screen.

The React 19 Upgrade Guide, published 25 April 2024, describes a change in how React handles a suspended component. React can commit the nearest fallback without waiting for the entire sibling tree, then schedule the suspended siblings to pre-warm their lazy requests. This is React 19 behavior, so check your version before assuming it applies to an earlier release.

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

Choose the smallest technique that fits

Compare candidate techniques on what they change, which kind of bottleneck they address, and what must stay stable for them to help. The table summarizes the decision.

Situation Candidate pattern What it changes What to check
Renders come from Effects that set state Simplify state and Effects Removes avoidable update chains Whether the value can be derived during render
A pure calculation is measured as slow and its inputs are stable useMemo Reuses a calculated value on later renders Complete, stable dependencies; no effect on the first render
A child is measured as expensive and its props often stay the same memo with stable props Can skip some child renders Own state and context still cause updates; new object or function props defeat the skip
Typing or another urgent interaction competes with expensive rendering useTransition or useDeferredValue Prioritizes urgent rendering The deferred section may briefly show older content
A rarely used component adds to initial code cost lazy with Suspense Delays code loading and shows a fallback Boundary placement and fallback suit the user flow

Work down the table in order. Removing work is usually better than reprioritizing it, and reprioritizing is usually better than caching it.

When a change does not help

If the Profiler still shows the same slow commit after a change, check these causes before adding another technique:

  • The measurement came from a development build. Repeat it in a production build with CPU throttling.
  • The child still re-renders because it has its own state or consumes changing context.
  • Props still receive a new object, array, or function on every parent render.
  • A useMemo or Effect dependency list is incomplete or changes on every render.
  • The slow cost is on the first render, which useMemo does not address.
  • The deferred content looks stale to users, which is the expected tradeoff of the deferral APIs and may need a clearer interface.
  • A Suspense fallback appears too often or covers too much, which points to boundary placement.

Once the cause is confirmed, change one thing at a time and profile again so you can see what the change actually did.

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

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.