React Performance Isn't About UseMemo – It's About Render Boundaries

javatuts1 pts0 comments

React Performance Isn’t About useMemo — It’s About Render Boundaries

The React Systems Newsletter

SubscribeSign in

React Performance Isn’t About useMemo — It’s About Render Boundaries<br>Why slow React applications usually come from state placement, update scope, and architectural coupling - not missing memoization hooks.

The React Systems Newsletter<br>May 19, 2026

Share

When a React application starts feeling slow, the reflex is almost universal.<br>Add useMemo.<br>Thanks for reading! Subscribe for free to receive new posts and support my work.

Subscribe

Wrap callbacks with useCallback.<br>Throw memo() around suspicious child components.<br>A filtered list recalculates?<br>useMemo.<br>A function gets recreated?<br>useCallback.<br>A component rerenders unexpectedly?<br>memo.<br>The logic feels reasonable. The tools are built for optimization, the problem appears local, and the fix seems quick.<br>The issue is that this often mistakes the symptom for the disease.<br>Most React performance problems do not begin because someone forgot a hook.<br>They begin because the application has weak render boundaries .

Rerenders Are Not the Enemy

A surprising amount of React optimization advice starts from a flawed assumption:<br>rerenders are bad.

They are not.<br>React was built around rerendering.<br>State changes.<br>React recalculates UI.<br>That is normal operation.<br>The real question is not:<br>“Why did this component rerender?”

The better question is:<br>“Why was this component involved in this update at all?”

Performance problems usually appear when updates become:<br>too wide

too frequent

too expensive

Artwork: Relativity<br>Author: M.C. Escher<br>Too wide

One input field changes.<br>Half the page updates.<br>Too frequent

Every keystroke propagates through a heavy component tree.<br>Too expensive

Rendering triggers:<br>sorting

filtering

formatting

rebuilding large objects

recomputing derived state

The rerender itself is rarely the core problem.<br>The update scope is.

Example: Healthy rerender

Perfectly fine:<br>function Counter() {<br>const [count, setCount] = useState(0)

return (<br>setCount(c => c + 1)}<br>{count}

}Rerenders happen.<br>Nothing is wrong.<br>Optimization would solve nothing here.

The useMemo Misconception

useMemo is useful.<br>But it is not architectural medicine.<br>A common mistake looks like this:<br>const filteredUsers = useMemo(<br>() =><br>users.filter(<br>user =><br>user.name.includes(query)<br>),<br>[users, query]<br>)This may be perfectly appropriate.<br>Or completely irrelevant.<br>The missing question is:<br>why is this calculation running so often?<br>If every keystroke causes:<br>table rebuilds

sidebar rerenders

chart updates

modal recalculations

then memoizing one filter does not solve the architecture.<br>It simply places a performance-shaped bandage over a broader design problem.

Artwork: The Treachery of Images<br>Author: René Magritte

The Real Problem: State With No Boundaries

One of the most common React performance failures is state lifted too high .<br>A single parent component ends up owning everything.<br>function DashboardPage() {

const [search, setSearch] =<br>useState('')

const [selectedRow, setSelectedRow] =<br>useState(null)

const [modalOpen, setModalOpen] =<br>useState(false)

const [loading, setLoading] =<br>useState(false)

const [tableData, setTableData] =<br>useState([])

const [errors, setErrors] =<br>useState({})<br>}Individually, nothing seems unusual.<br>Collectively, it becomes dangerous.<br>Now every small state change flows through the same parent.<br>Search input changes?<br>Large subtree rerenders.<br>Modal opens?<br>Table participates.<br>Loading flag updates?<br>Entire layout recalculates.<br>Developers often respond by layering optimization:<br>memo

useCallback

useMemo

The app becomes slightly quieter.<br>The architecture stays equally tangled.<br>The problem was never missing memoization.<br>The problem was missing boundaries.

State Should Live Close to Ownership

Good React optimization often begins with a surprisingly unglamorous move:<br>move state closer to where it actually belongs.<br>Bad:<br>Page<br>├── SearchBox<br>├── DataTable<br>├── Sidebar<br>└── Modal

Everything depends on Page state.Better:<br>Page<br>├── SearchSection<br>│ └── search state<br>├── DataTable<br>│ └── row selection state<br>└── Modal<br>└── visibility stateNow updates become localized.<br>Opening a modal does not rebuild unrelated UI.<br>Typing into search does not redraw the entire page.<br>Render boundaries become narrower.<br>React performs less work because less work exists.

Artwork: Broadway Boogie Woogie<br>Author: Piet Mondrian

Expensive Work Belongs Off the Hot Path

Not all rendering work carries equal cost.<br>Changing button text?<br>Cheap.<br>Sorting 20,000 rows after every keystroke?<br>Not cheap.<br>This is where optimization becomes practical.<br>Before adding hooks, map the hot path .<br>Ask:<br>What happens after the user types?<br>What changes after clicking a row?<br>Which components receive new props?<br>Which calculations rerun?<br>Once the update flow becomes visible, solutions become clearer.<br>Sometimes:<br>separate input from results<br>Sometimes:<br>virtualize large lists<br>Sometimes:<br>move formatting...

react state usememo const performance boundaries

Related Articles