The React Profiler Demystified

This post is for Day 6 of Mercari Advent Calendar 2024, brought to you by Sam Lee from the Mercari Seller 3 team.

When building web applications, performance can make or break the user experience. With large applications like mercari.jp, we as engineers have to be more mindful of its performance. Whenever there is a stutter or jank, I always find myself not knowing where to start. Was that just a slow API call or was there an expensive calculation? This is where the React Profiler comes in. It’s a tool that can help you pinpoint performance bottlenecks easier than before. In this post, I’m going to explain what the React Profiler is and dive into a hypothetical example.

What is the React Profiler?

The React Profiler is part of React’s Developer Tools browser extension that helps you measure the performance of your React app. When an application becomes complex with many components re-rendering in response to state or prop changes, the Profiler gives you the ability to zoom in on these re-renders. It breaks down why these re-renders are happening and highlights performance issues like excessive renders or unnecessary computations.

A hypothetical example

Telling you the different parts of the profiler probably won’t be too fun, so let’s learn by example and see how the React Profiler can be used.

Let’s assume that you’re working on a hypothetical React application and tinkering with the development build in your free time. This is when you notice a brief but annoying jank after clicking on a button that displays a list of items.

What happens on the development build may not happen on the production build, so you head on over to your production site and open up the Chrome DevTools’s Performance tab. You hit record, click the button in question, and then watch as the timeline loads…only to find that there’s a whopping 100 milliseconds between when you click the button and the next UI update—that is 10 frames per second (FPS) when playing your favorite game.

In order to find out what causes this, you redo the whole thing again but now with your handy React Profiler. Hit record, click the button and hit stop.

The upper left section of the profiler where the blue record button is located

You filter out all commits, which are changes that React applied to the DOM (Domain Object Model), that took less than 20 milliseconds, because they’re likely too small to matter.

An popup window in the React Profiler showing an option that says "Hide commits below" followed by a textbox which lets the user specify the duration in milliseconds.

You only want the “frames” (commits) causing your app to drop to 10 FPS. One particular commit stuck out towering over everything else.

A commit bar graph showing the highest bar in yellow
A commit bar graph displaying the durations of each commit by height. Commit is the phase when React applies changes directly to the DOM.

You clicked on the commit which updated the Flame graph, a hierarchical visualization showing the time it took to render a component relative to its children. Invented in 2011 (quite recent!) Flame graphs were created originally for showing the CPU usage of function calls in MySQL.

A flame graph with the top bar being the parent and its children below it. Duration of a render is shown by its width and denoted by the right most number on the bar.

The component highlighted in yellow is what caused the particular commit and the slow render. Upon closer inspection of the time it took to render, you see that the 0.9ms—the time it took to render just the parent component is only a tiny fraction of 87.8ms—the time it took in total to render the parent component and its children. It’s not that the component is inefficient; it’s simply trying to render too many children at once, causing the component to take 87.8 milliseconds!

There are multiple potential solutions. One solution is pagination of the list—displaying the list one manageable page at a time. Another option is to virtualize the list—rendering only a portion of the list at any given time, depending on what’s visible on the screen.

You then pitch the issue, cause, and solutions to the team.

Final thoughts

I hope that example helped in demystifying just a bit of what the React Profiler is. Do keep in mind that performance bottlenecks come in all shapes and sizes. Some are caused by unnecessary re-renders, others by inefficient rendering strategies or sheer scale. In my personal experience, re-renders of not just a component but the entire page are the most common. But of course your mileage may vary and knowing how to approach these problems can make all the difference.

Tomorrow’s article will be by @cherry. Please look forward to it!

  • X
  • Facebook
  • linkedin
  • このエントリーをはてなブックマークに追加