React is lightning fast. Often, it’s fast enough to make it hard to re-create performance issues in a sandbox on our machines. Consequently, we might believe that our web app will run smoothly, no matter what we do.
But it’s a trap. Developers usually use strong machines and internet connections to build web apps. However, our high-performance environments might hide from us performance issues that can hurt our users. Unlike developers, many of our users access our web apps using mid-tier mobile smartphones from locations with bad internet connections.
But your app’s speed and performance matter and have a major effect on conversion rates.
If we don’t analyze performance, because of our relatively strong machines and internet connections, we will not know about performance issues that hurt our users and subsequently hurt our conversion rates.
This article will present a sandbox with a performance issue caused by a small React anti-pattern. Then, I’ll show you how it can be detected using the library Why Did You Render and how to resolve the issue.
Reproducing performance issues
The following app simulates a header that changes its size as the user scrolls. A list with many rows is used to represent a medium-to-large application.
Note: In practice, we would want to make such a long list a virtual list, but in our case, we only use it to simulate an application.
Our app has performance issues with the animation of the header’s height change while scrolling.
Here is the sandbox:
Performance Issue in React
CodeSandbox is an online editor tailored for web applications.
To reproduce the performance issue on powerful machines, I suggest slowing the browser down artificially by throttling the CPU. You can do that in Chrome using the Performance tab.
It’s great practice to use this important tool to run your app this way when working on performance issues to see how things work on slower devices:
Can you detect what causes the performance issue in the code?
Detecting the bug with Why Did You Render
The usual way to spot the issue is by using React or Browser dev-tools. Many great articles demonstrate how to debug that in-depth.
In short, we open the browser dev-tools (in this case, Chrome’s dev-tools, but other modern browsers have similar features) and record a few seconds while reproducing the issue:
Then we stop the profiler. We can immediately see that the Main
component, with its many children, re-renders on every single scroll event, causing janks in the application.
But before we jump to the code and try to understand what’s wrong with Main
, let’s try Why Did You Render.
What is Why Did You Render?
Why Did You Render is a library created by Welldone Software that detects why a component in your app is re-rendering through monkey-patches in React and will notify you about potentially avoidable re-renders.
Note: Make sure not to add the library in production, as it slows React and even might cause it to break in certain edge-cases. Only turn it on when you debug performance issues.
First, we add the library from npm
:
npm install @welldone-software/why-did-you-render --save
Next, we add a wdyr.js
file to the root of our project:
import React from "react"; // Make sure to only include the library in development if (process.env.NODE_ENV === "development") { const whyDidYouRender = require("@welldone-software/why-did-you-render"); whyDidYouRender(React, { trackAllPureComponents: true }); }
And third, we import wdyr.js
as the first import of our application in index.js
:
import "./wdyr"; // <-- first import import React from "react"; import ReactDOM from "react-dom"; ...
For a detailed installation guide, look at the readme.
Here is a sandbox with the library installed. Now, if you scroll through, you will get the following information in your console:
Main
was “re-rendered because of props changes”- The prop in question is
style
style
has received different objects that are equal by value:
{paddingTop: 350} !== {paddingTop: 350}
Main
is re-rendered byApp
, andApp
gets re-rendered because of a trigger of auseState
hook
As you can see, we get a very clear picture of why Main
got re-rendered. Based on this information, we can infer that the problem is caused by a widespread React anti-pattern when dealing with pure components.
Let’s look at App.js
:
export default function App() { const headerHeight = useHeaderScroll({ min: 50, max: maxHeaderHeight, maxOffset: 3000 }); return ( <div className="App"> <Header style={{ height: headerHeight }} /> <Main style={{ paddingTop: maxHeaderHeight }} /> </div> ); }
When a scroll happens, the hook useHeaderScroll
causes App
to re-render. This re-render causes the Main
element to be re-created:
<Main style={{ paddingTop: maxHeaderHeight }} />
Now, because Main
is a pure component, it wasn’t supposed to re-render when App
re-renders, because seemingly, its props are the same as in the previous render of App
. However, in reality, the style
prop is a new object on every render:
{ paddingTop: maxHeaderHeight } !== { paddingTop: maxHeaderHeight }
Debugging the React app
An easy solution to the performance issue would be to pass only the relevant value to Main
instead of the style
object.
Let’s change:
<Main style={{ paddingTop: maxHeaderHeight }} />
To this:
<Main paddingTop={paddingTop} />
Main
will no longer re-render because its only prop is always paddingTop={350}
.
Now we just have to make sure that Main
is adjusted accordingly to expect paddingTop
as a prop instead if style
:
const Main = ({ paddingTop } /* instead of {style} */) => {
You can find the corrected application without performance issues in the following sandbox.
Conclusion
Using Why Did You Render can help identify bugs in your React app very effectively, even in places one would not normally look for them. It also reports in great detail so you can know exactly what went wrong. My suggestion is to at least run your initial page load with it to see how can you speed it up in minutes. Thanks for reading.
Cut through the noise of traditional React error reporting with LogRocket
LogRocket is a React analytics solution that shields you from the hundreds of false-positive errors alerts to just a few truly important items. LogRocket tells you the most impactful bugs and UX issues actually impacting users in your React applications.

Focus on the React bugs that matter — try LogRocket today.