In this article we'll measure the performance of an example React app with both the Profiler tab in React DevTools, and the Profiler component.

You’ve just created a brand new React app, but you want to understand its performance characteristics before shipping it to your customers. While you can use the browser’s User Timing API to measure the rendering times of your components, there’s a better alternative created by the React team: the Profiler API, and a Profiler tab in React DevTools.

The Profiler API is the recommended way of measuring the rendering times of our components, because it’s fully compatible with features such as time-slicing and Suspense.

In this article we’ll measure the performance of an example React app with both the Profiler tab in React DevTools, and the Profiler component.

Using the Profiler Tab from React DevTools

If we are working on our React app in development mode, we can use the Profiler tab in React DevTools to record parts of its execution, and then analyze all the updates that React made. (If we want to use the Profiler tab on a production app, we need to make some changes to our config.)

To profile our app, we just need to switch to the Profiler tab, and press the Record button to start profiling:

Start profiling by pressing the Record button

We’ll then perform actions on our app, and press the Record button again to stop profiling. The DevTools will show us each of the updates that happened while we were recording, using a fancy flame chart:

Stop profiling by pressing the Record button again

If you are not familiar with this way of representing performance data, you may be wondering what all these colored bars mean. Let’s break it down.

Every time any of our components render, React compares the resulting tree of components with the current one. If there are changes, React will take care of applying them to the DOM in a phase called commit.

The colored bars we’re seeing at the top are commits that happened while we were recording. The yellow/orange bars are the ones with higher rendering times, so we should probably pay extra attention to them:

Commits made while we were recording

If we click on one of those commits, the flame chart below will be updated, showing the components that changed in that commit as horizontal bars. The longer the bar, the more time it took for that component to render:

Flame chart of component rendering times

The chart shows the root component at the top, with its children sitting below in hierarchical order. The number shown inside each bar represents the time it took to render the component and its children. When we see something like RangeButtons (0.2ms of 1.8ms), it means that RangeButtons took 0.2ms to render, while RangeButtons plus its only child ButtonGroup took 1.8ms. That means ButtonGroup must have taken ~1.6ms to render, which is confirmed when we look at the bar below that says ButtonGroup (0.3ms of 1.6ms).

Another cool thing we can do here is click on the bar for a certain component. Not only will the flame chart focus on the selected component, but the pane on the right will also show us how many times it has rendered for the lifetime of the app:

StockChart app commit times

The Profiler tab in React DevTools is a great way of inspecting how our app is performing without needing to change our code. Just by recording key interactions, we’ll be able to know where rendering time is going, and identify bottlenecks that make our app sluggish.

Using the Profiler Component

If we want to have programmatic access to the performance measurements of a specific component, we can use the Profiler component. It wraps part or all of our app tree, and gives us metrics on how long it took for that tree to render.

The first thing we have to do to use the Profiler component is to import it:

import React, { Profiler } from "react";

The Profiler component can then be used to wrap any part of our tree of components:

// CustomStockChart.js

const CustomStockChart = props => {
   // ...

  return (
    <Profiler id="StockChart" onRender={logTimes}>
        {/* ... */}

const logTimes = (id, phase, actualTime, baseTime, startTime, commitTime) => {
  console.log(`${id}'s ${phase} phase:`);
  console.log(`Actual time: ${actualTime}`);
  console.log(`Base time: ${baseTime}`);
  console.log(`Start time: ${startTime}`);
  console.log(`Commit time: ${commitTime}`);

export default CustomStockChart;

When CustomStockChart renders, the Profiler's onRender callback will be invoked with a bunch of useful information. In our example, it’ll print something like this to the console:

StockChart's mount phase:
Actual time: 7.499999995867256
Base time: 7.1249999981955625
Start time: 384888.51500000054
Commit time: 384897.5449999998

StockChart's update phase:
Actual time: 0.3500000038766302
Base time: 7.075000001175795
Start time: 385115.2050000001
Commit time: 385116.22499999974

The meaning of each of these arguments is explained in the documentation for the Profiler API. In the real world, instead of logging them to the console, you would probably be sending them to your backend in order to get useful aggregate charts.

Anyways, be sure to spend time understanding these two new tools in your arsenal, as they’ll prove invaluable when trying to identify performance issues in your React apps!

Keep Reading

About the Author

Blanca Mendizábal Perelló

Blanca is a full-stack software developer, currently focused on JavaScript and modern frontend technologies such as React. Blanca can be reached at her blog or @blanca_mendi on Twitter.

Related Posts


Comments are disabled in preview mode.