Profiling React Performance With Flamegraphs
Cover photo by Markus Spiske on Unsplash
The React DevTools flamegraph is the only tool that actually matters when your app feels sluggish. I stop guessing about why a component re-renders and just look at the data because the browser never lies.
Why the flamegraph matters
Most people try to fix performance by wrapping everything in React.memo. That is usually a mistake. It adds complexity, hurts readability, and often does absolutely nothing because your state updates are still happening in the wrong place. Before you touch a single line of code, you need to see what is happening during a render cycle. The flamegraph shows you exactly which components committed, how long it took, and why they were triggered.
How I start
Open your DevTools, go to the Profiler tab, and hit that record button. Click around your app like a regular user would. Stop the recording. Now look at the chart. If you see a giant tree of components turning grey or blue, you found your culprit. I look for thin bars that should not be there.
Here is a simple example of a component that might be causing you grief:
function Parent({ data }) { const [count, setCount] = useState(0); return ( <div> <button onClick={() => setCount(count + 1)}>Increment</button> <ExpensiveList items={data} /> </div> );}Every time you click that button, ExpensiveList re-renders. (It happens even if the data does not change, which is annoying). If you see ExpensiveList in your flamegraph after every click, you know exactly where to apply optimization strategies like useMemo or moving state down.
The “it is too hard” objection
“But the flamegraph is confusing to read and doesn’t always tell me what changed.” I hear this constantly. You are right, it is not always intuitive. Sometimes the tool is overkill for a small list, or the component tree is so deep that the visualization becomes a mess. When the app is truly tiny, I stick to simple console.log debugging or just checking the component inspector. Use the right tool for the size of your headache.
A practical tip for your workflow
I always start by looking for the most expensive components at the top of the commit list. If a component takes 20ms to render, it is a problem. If it takes 0.2ms, ignore it. Do not fall into the trap of premature optimization (a classic mistake I have made more times than I care to admit). Keep your eye on the big wins first.
Why I avoid heavy abstraction
I find that when we hide component complexity behind too many custom hooks or complex context providers, the flamegraph becomes much harder to parse. Sometimes, clear and boring code is easier to profile than fancy functional patterns. It is just a fact.
Are you checking your production bundle size or just your local DevTools? There is a difference between what runs on your machine and what your users actually experience on their phones. I usually try to profile both if I suspect something is really broken. It is a simple step, but it reveals a lot about how your code behaves in the real world.