HomeAbout Me

React Performance 1: Element Optimization

By Daniel Nguyen
Published in React JS
July 02, 2025
3 min read
React Performance 1: Element Optimization

Element Optimization

Elements are the fundamentals building blocks of React UI. It was the first we started with when you started learning React in this workshop series. And React has some smarts under the hood we can take advantage of when we’re rendering elements.

What we’re going to be learning about in this exercise can be summed up as:

If you give React the same element you gave it on the last render, it wont bother re-rendering that element. – @kentcdodds

Here’s a simple example:

function Message({ greeting }) {
console.log('rendering greeting', greeting)
return <div>{greeting}</div>
}
function Counter() {
const [count, setCount] = useState(0)
const increment = () => setCount((c) => c + 1)
return (
<div>
<button onClick={increment}>The count is {count}</button>
<Message greeting="Hello!" />
</div>
)
}

With this set up, we’ll get a log every time the counter is incremented. Meaning the <Message /> component is rerendered every time its parent rerenders (which is to be expected). But this is unnecessary since the greeting component won’t ever change what it’s rendering.

What if I refactored things a little bit. For example:

function Message({ greeting }) {
console.log('rendering greeting', greeting)
return <div>{greeting}</div>
}
const message = <Message greeting="Hello!" />
function Counter() {
const [count, setCount] = useState(0)
const increment = () => setCount((c) => c + 1)
return (
<div>
<button onClick={increment}>The count is {count}</button>
{message}
</div>
)
}

In this situation, the <Message /> component only renders once and won’t rerender whenever the count changes. This is because we’re giving React the same element every time. How does this work?

When React is given the same element it was given on the last render, it won’t bother rerendering that element again because that would be pointless. It just keeps the same element and moves on.

This is a simple, but powerful optimization that can help you avoid unnecessary rerenders in your application. It’s not always possible to do this, but you’d be surprised how simply restructuring your components can make this possible more and more often.

The optimization we’re going to be learning about in this exercise is explained with examples in One simple trick to optimize React re-renders. Feel free to check that out for a more in depth explanation for how this works.

Reusing Elements

👨‍💼 We’re going to be building a simple user interface and in the process we want to limit unnecessary renders as much as possible (not because it’s necessary for this specific application, but because it’s a useful exercise for you to learn how to do for when it is necessary).

Right now, when you click the counter button, both the App and Footer components rerender.

But the Footer component doesn’t change so that render is unnecessary. So please take advantage of React’s element optimization by creating a Footer element outside the App so you can render that inline in the App component.

Make sure to check the before/after using the React DevTools so you know that your work is making a difference.

Element Props

🧝‍♂️ I’ve enhanced our app a bit to add some buttons that will control the color of our footer. This means we can’t just render the component outside the App component since we need to pass the color as a prop. Feel free to

check my work
.

👨‍💼 Yep, to make it work, Kellie had to render the <Footer /> in <Main /> which means the <Footer /> gets rerendered every time we click the count button even if the color doesn’t change.

So your job is to restructure things so the Footer only re-renders when the color is changed.

Make sure to pull up the React DevTools to be certain incrementing the count doesn’t trigger a rerender of the footer.

Context

👨‍💼 We now have a counter inside the App component and every time that count is incremented, we trigger a rerender of the Footer component! Have we lost the ability to take advantage of React’s element optimization? No!

Instead of accepting the color via props, we can place it in a context provider that the Footer can consume. And with that, the footer no longer accepts any dynamic props so we can move it outside the App component again, which means the only way it can be rerendered is if the context changes (which is exactly what we want).

When you’re done, make certain that the Footer only rerenders when the color changes and not when the counters are incremented.

Memoize Elements

👨‍💼 Now we want our users to be able to type their name and have that show up in the footer. But we don’t want to use context for the name like we do with the color just yet. We just want to be able to pass the name as a regular prop. But we still want to make sure that the Footer only rerenders when it should and no more. So you’re going to need to combine useMemo with this React element optimization.

Remember that useMemo allows you to get the same value back if the dependencies haven’t changed. Well, you can create a React element that depends on the name and render that:

const someElement = useMemo(() => <MyComponent myProp={myProp} />, [myProp])

Give that a shot! (Double-check your work with the React DevTools).

Memoize Components

🦉 Memoizing elements is such a common idea that React has a built-in optimizer called memo which allows you to memoize an entire component based on its props:

import { memo } from 'react'
const MyComponent = memo(function MyComponent(props) {
/* only rerenders if props change */
})

This effectively turns the props object into a dependency array a la useMemo and applys wherever the component is rendered.

🧝‍♂️

I’ve moved the Footer out of useMemo
so we’ve got it rendering unnecessarily again.

👨‍💼 You’re going to use memo to get the Footer back into a position where it only renders when the name or color change. Good luck!

📜 Check out the memo docs

import { createContext, memo, use, useState } from 'react'
import * as ReactDOM from 'react-dom/client'
const ColorContext = createContext<string | null>(null)
function useColor() {
const color = use(ColorContext)
if (!color) throw new Error('ColorContext not found')
return color
}
const Footer = memo(function FooterImpl({ name }: { name: string }) {
const color = useColor()
return (
<footer style={{ color }}>
I am the ({color}) footer, {name || 'Unnamed'}
</footer>
)
})
function Main({ footer }: { footer: React.ReactNode }) {
const [count, setCount] = useState(0)
const increment = () => setCount((c) => c + 1)
return (
<div>
<button onClick={increment}>The count is {count}</button>
{footer}
</div>
)
}
function App() {
const [appCount, setAppCount] = useState(0)
const [color, setColor] = useState('black')
const [name, setName] = useState('Kody')
return (
<ColorContext value={color}>
<div>
<div>
<p>Set the footer color:</p>
<div style={{ display: 'flex', gap: 4 }}>
<button onClick={() => setColor('black')}>Black</button>
<button onClick={() => setColor('blue')}>Blue</button>
<button onClick={() => setColor('green')}>Green</button>
</div>
</div>
<div>
<p>Set the footer name:</p>
<label>
Name:
<input
value={name}
onChange={(e) => setName(e.currentTarget.value)}
/>
</label>
</div>
<button onClick={() => setAppCount((c) => c + 1)}>
The app count is {appCount}
</button>
<Main footer={<Footer name={name} />} />
</div>
</ColorContext>
)
}
const rootEl = document.createElement('div')
document.body.append(rootEl)
ReactDOM.createRoot(rootEl).render(<App />)

Tags

#React

Share

Previous Article
React Performance: Introduction

Table Of Contents

1
Element Optimization
2
Reusing Elements
3
Element Props
4
Context
5
Memoize Elements
6
Memoize Components

Related Posts

React Testing 8: Testing custom hook
September 09, 2025
1 min
© 2025, All Rights Reserved.
Powered By

Quick Links

About Me

Legal Stuff

Social Media