Back to blogs

Stop Unnecessary React Re-renders and Make Your UI Instantly Faster

October 12, 2025
7 min read
Stop Unnecessary React Re-renders and Make Your UI Instantly Faster

If you’ve ever built something in React and felt like the UI isn’t snappy enough. Even though everything “works” - there’s a good chance your components are re-rendering more than they should.

It’s one of those things that quietly slows down your app without breaking anything. You don’t notice it until the app grows, and suddenly simple clicks or input typing start feeling laggy.


I’ve been there too. In this post, I’ll show you the way I handle this problem — using React’s Profiler, React.memo, and selectors to track and stop unnecessary re-renders. I’ll also share some real examples of how I fixed them in my projects.


Let’s start simple and go step-by-step.


Why Unnecessary Re-renders Matter

React re-renders components whenever props or state change. That’s the core behavior, and it’s good — that’s how updates appear on screen.


But what happens when components re-render even though their data hasn’t changed? That’s wasted work. It’s like repainting a wall that already looks fine.


Too many unnecessary re-renders can:

  1. Slow down user interactions
  2. Make animations and transitions less smooth
  3. Waste CPU and memory
  4. Cause random flickers or micro-lags in UI


The frustrating part is: these re-renders aren’t visible in code. You only feel them in performance or frame drops.

So, the first step is finding which components are causing trouble.


Step 1: Use React Profiler to Find the Problem

Before you optimize anything, measure it. The React DevTools Profiler is perfect for this.


When you record a render session, the Profiler shows you which components rendered and how long each one took.


Here’s what I usually look for:

  1. Components that re-render every time, even when no prop changes.
  2. Components taking unusually long render times.
  3. Chains of re-renders — where one parent update triggers 10 child re-renders.


If you’re not sure what’s re-rendering, just add a console.log('render') inside the component. You’ll immediately see how often it runs.

Once you identify the culprit, it’s time to optimize.


Step 2: Use React.memo to Skip Unnecessary Updates

The first optimization tool is React.memo.


It’s a higher-order component that tells React:

“If my props haven’t changed, skip rendering this component.”


Example:

const MyComponent = React.memo(function MyComponent({ title }) {
console.log('render');
return <h1>{title}</h1>;
});

Now MyComponent will only re-render if title changes.


It sounds simple, but it’s incredibly powerful. If you use it correctly, you can cut down re-renders drastically.


However, there are two things to remember:

  1. Objects and arrays are compared by reference, not by value.
  2. That means if you pass a freshly created object like { name: 'John' } every render, React will think it changed even if it didn’t.
  3. Solution: memoize objects or move them outside the render.
  4. Don’t memo everything blindly.
  5. If your component is small and cheap to render, memoization might actually add overhead.


The trick is to use memo where you notice unnecessary re-renders — not everywhere.

You can also pass a custom comparison function to React.memo if you want more control, but in most cases, the default shallow compare works fine.


Step 3: Use Selectors to Reduce Re-renders from Global State

Sometimes, the cause of re-renders isn’t props — it’s global state.

If you’re using Redux, Zustand, Jotai, or even Context, components can re-render every time any part of the global state changes, even if they only depend on a tiny piece of it.

That’s where selectors come in.

Selectors let you subscribe to exactly the part of the state that matters. When other parts change, your component stays still.


Example using Redux-style logic:

const selectVisibleItems = state => state.items.filter(i => i.visible);

function ItemList() {
const items = useSelector(selectVisibleItems);
return items.map(item => <Item key={item.id} item={item} />);
}


Here, the ItemList component only re-renders when the list of visible items changes — not when some unrelated user or theme state updates.

If you’re using Zustand or Context, a similar idea applies: subscribe only to what you need.


Selectors are one of the most effective ways to reduce noise in your render tree.


Step 4: Real Examples and Fixes

Let’s see a few real-world situations I faced and how I fixed them.


Example 1: Parent Re-renders Cause Child Re-renders

function Parent() {
const [count, setCount] = useState(0);
const user = { name: 'Alice' };

return (
<>
<button onClick={() => setCount(c => c + 1)}>+</button>
<Child user={user} />
</>
);
}

function Child({ user }) {
console.log('Child render');
return <div>{user.name}</div>;
}

At first glance, it looks fine. But every time you click the button, Child re-renders too.

Why? Because the user object is re-created on every parent render. Even though it’s the same data, it’s a new reference.

Fix:

const user = useMemo(() => ({ name: 'Alice' }), []);

Now user is stable. Wrap Child in React.memo, and it’ll only render once unless user changes.


Example 2: Passing Functions Down

Another common one — you pass functions down as props:

function Parent() {
const [count, setCount] = useState(0);
return <Child onClick={() => setCount(count + 1)} />;
}

The inline function () => setCount(count + 1) is recreated every render.

Fix:

const handleClick = useCallback(() => setCount(c => c + 1), []);
return <Child onClick={handleClick} />;

Now the function reference is stable, and the child won’t re-render unnecessarily.


Example 3: Context Updates Everything

If you’re using React Context, be careful with what you put inside the provider.

<ThemeContext.Provider value={{ theme, toggleTheme }}>
<Header />
<Footer />
</ThemeContext.Provider>


Each time theme changes, the whole provider value changes (since it’s a new object), and every consumer re-renders — even those that only use toggleTheme.

Fix:

const contextValue = useMemo(() => ({ theme, toggleTheme }), [theme, toggleTheme]);
<ThemeContext.Provider value={contextValue}> ... </ThemeContext.Provider>

Or, better, split the context — one for the theme, one for the toggle function.


Example 4: Lists and Keys

If you render lists with index keys, React can mix up identities, forcing unnecessary updates:

{items.map((item, i) => <Row key={i} item={item} />)}

Always use stable keys, ideally an ID:

{items.map(item => <Row key={item.id} item={item} />)}

Then wrap Row in React.memo. This small change makes a big difference when dealing with hundreds of list items.


Step 5: Re-Profile and Re-Test

After you apply these optimizations, always go back to the Profiler.

You’ll notice the difference immediately — fewer components re-render, faster response times, and smoother interactions.

Optimization is iterative. Don’t try to fix the whole app at once. Start with the most noticeable slow areas, test them, and keep refining.


Summary: My Go-To Approach

Here’s my usual flow when I feel something’s “laggy”:

  1. Profile first. Find which components are over-rendering.
  2. Use React.memo. Wrap components that re-render for no reason.
  3. Memoize values and callbacks. Use useMemo and useCallback for stable references.
  4. Use selectors wisely. Make components subscribe only to the state they actually use.
  5. Split context providers. Don’t let a single value update the whole tree.
  6. Fix list keys and props. Keep them stable.


Final Thoughts

The goal isn’t to stop all re-renders — React needs them to work. The goal is to stop the wasted ones.

Once you start spotting patterns, it becomes second nature. You’ll catch them before they cause issues.

After I started following this approach, my UIs felt instantly faster. The app “snappiness” improved without changing a single visual thing.

It’s one of those invisible optimizations that users can’t see — but they feel it. And honestly, that’s what makes great front-end work stand out.


React re-rendersstop React re-rendersReact performance optimizationhow to stop React re-rendershow to improve React performanceoptimize React UIReact memo tutorialReact Profiler guidehow to use React memofix slow React apphow to make React fasterReact selectors optimizationprevent unnecessary renders in ReactReact performance tipshow to debug React re-rendersReact memo vs useMemo

Recent Blogs

View All