HomeAbout Me

React Performance: Element Optimization

By Daniel Nguyen
Published in React JS
July 11, 2025
2 min read
React Performance: Element Optimization

🎯 React Element Optimization: How to Avoid Unnecessary Renders and Improve Performance

React is an incredibly powerful UI library—but with great power comes the responsibility to keep things performant. One of the most common culprits of sluggish React apps? Unnecessary re-renders.

This post walks through how React handles re-renders, and more importantly, how you can optimize your components using techniques like memoization, JSX reuse, and context. We’ll build a simple app and use React DevTools to understand what causes a component to re-render—and how to stop it when we don’t need it.

Let’s dive in!


⚛️ React Elements: The Basics

In React, elements are the fundamental building blocks of your UI. JSX gives you a convenient way to define these elements, but under the hood, they’re just objects.

Here’s a quote to keep in mind:

“If you give React the same element you gave it on the last render, it won’t bother re-rendering that element.” — Kent C. Dodds

Let’s see this in action.

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

With this setup, clicking the button logs every time, even though the <Message /> component receives the same props.

Now look at this slight change:

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

Now the Message component only renders once—even when the count changes. Why? Because the JSX element is created once outside the render function and reused across renders.

💡 React recognizes it’s the same element and skips the update.


🔁 Reusing Elements to Avoid Renders

Let’s take this further in a real-world scenario.

Imagine your app has a static <Footer /> that doesn’t depend on state:

function Footer() {
console.log('rendering footer')
return <footer>© 2025 React Workshop</footer>
}
function App() {
const [count, setCount] = useState(0)
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Click</button>
<Footer />
</div>
)
}

Every button click re-renders <Footer />, even though it doesn’t need to.

✅ Fix it by hoisting:

const footer = <Footer />
function App() {
const [count, setCount] = useState(0)
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Click</button>
{footer}
</div>
)
}

Boom 💥! The footer is no longer re-rendered needlessly.


🎨 When Props Change: Pass Elements as Props

Now suppose the Footer has a dynamic prop like a color:

function Footer({ color }) {
console.log('rendering footer with color', color)
return <footer style={{ color }}>Thanks for visiting</footer>
}

We can no longer define the JSX statically outside the render function. But we can still optimize.


🌐 Optimize With React Context

Let’s use React Context to avoid prop drilling and isolate re-renders.

Step 1: Create a Color Context

const ColorContext = React.createContext()

Step 2: Provide color via context

function App() {
const [color, setColor] = useState('black')
return (
<ColorContext.Provider value={color}>
<Main />
</ColorContext.Provider>
)
}
function Footer() {
const color = useContext(ColorContext)
console.log('rendering footer with color', color)
return <footer style={{ color }}>Thanks!</footer>
}

Now, Footer doesn’t receive props, so you can hoist it again and React will only re-render it when context changes.


🧠 Memoizing Elements with useMemo

What if you must pass props (like name) but still want to optimize?

React’s useMemo() is your friend:

const memoizedFooter = useMemo(() => {
return <Footer name={name} />
}, [name])

Now <Footer /> is only re-rendered when name changes—not on every unrelated state update.

💡 Use React DevTools Profiler to confirm re-render behavior.


⚡ Component-Level Memoization with React.memo

React even gives you a component-level memoizer: React.memo.

const Footer = memo(function FooterImpl({ color, name }) {
console.log('rendering footer')
return <footer style={{ color }}>Hello, {name}!</footer>
})

This acts like a built-in useMemo() for components: React skips re-renders unless props have changed.

This is a cleaner and more robust solution than manually memoizing every element.

⚠️ Be careful: memo uses Object.is under the hood, so if you’re passing complex objects, you may need to memoize them too.


🔍 Final Thoughts

Here’s a quick summary of how to optimize React elements and avoid unnecessary re-renders:

  • Reuse JSX: Move static elements outside your component.
  • Use Context: Avoid prop drilling and isolate re-renders.
  • Memoize Elements: Use useMemo() when JSX depends on state.
  • Memoize Components: Use React.memo() for full component optimization.
  • Check With DevTools: Always verify with profiling tools.

React is smart—but it’s not omniscient. A few simple tweaks to how and where you define your elements can yield a significant performance boost.

So next time you’re profiling your app and wondering why something is rendering when it shouldn’t—remember this guide 😉


📚 Further reading:


Want a live example or need help debugging your own components? Drop a comment or reach out!

Happy optimizing ⚛️🔥



Tags

#ReactPerformance

Share

Previous Article
React Performance: Introduction

Table Of Contents

1
⚛️ React Elements: The Basics
2
🔁 Reusing Elements to Avoid Renders
3
🎨 When Props Change: Pass Elements as Props
4
🌐 Optimize With React Context
5
🧠 Memoizing Elements with useMemo
6
⚡ Component-Level Memoization with React.memo
7
🔍 Final Thoughts

Related Posts

React Performance: Windowing
July 17, 2025
2 min
© 2025, All Rights Reserved.
Powered By

Quick Links

About Me

Legal Stuff

Social Media