Primitives to measure your React application's performance using
Table of Contents
Install the package
First we will need to install the npm package.
$ yarn add @shopify/react-performance
Track some stats
@shopify/react-performance library automatically handles creating a shared
Performance instance (from
@shopify/performance) for you when it is imported. This
Performance instance automatically wraps the browser's built in performance and connection information and calculates some basic page-load stats. A long as we import
@shopify/react-performance somewhere in our app the instance will be there for us.
That said, we will need to add some calls to the
usePerformanceMark hook to our application in order to help
@shopify/react-performance determine when a page is fully loaded and React has finished mounting components.
//Cats.tsx;;// Represents an entire page of our application
note: In production apps, it is important to always have one
usePerformanceMark on each page level component. Otherwise, client-side navigations may not be recorded properly. When
usePerformanceMark calls are omitted in some pages but not others, it is common to see strange results such as:
- Navigation data which makes it look like some page takes infinitely long to load
- Navigation data in which some page sometimes randomly takes several times longer than usual
- Missing data for a particular page
Display real-time navigation data
To demonstrate the data we have so far, we'll create a component called
LastNavigationDetails and use it to display some basic data about our app's performance.
// LastNavigationDetails.tsx;;// A component which displays simple performance stats about the last navigation performed by the user
We can render this component anywhere in our application, but lets do so in our top level App component.
// App.tsx;;// our component from above which calls `useNavigationListener`;// our component from above which calls usePerformanceMark;// the top level component of our application
Now our developers will have access to up-to-date information about how long initial page-loads and incremental navigations take to complete without needing to open their devtools or dig into DOM APIs manually.
Send report data to a server
Performance metrics data is only truly useful when it can be aggregated and used to make smart optimization decisions or warn you when your app is beginning to become slow. To aggregate such data you'll need to be able to send it to a server.
@shopify/react-performance provides the
usePerformanceReport hook to facilitate this.
// App.tsx;;/*** The top level component of our application, it should handle rendering all top level providers and determining what pages to render*/
Assuming our App sets up some
<PerformanceMark /> components, our code will send a performance report to the
/performance_report url as JSON. Generally, the easiest way to set up a server to make use of the reported data is to use one of our server-side companion libraries.
Process report data and forward it to StatsD
Using Node (
Using Rails (
A custom hook which takes in callback to invoke whenever the
Performance context object emits a lifecycleEvent.
A custom hook which takes in a callback to invoke whenever a navigation takes place, whether it is a full-page load (such as the result of
location.assign) or an incrmental load (such as the result of
router.push in a ReactRouter app).
A component which takes a
stage and an
id and uses them to generate a tag for a call to
window.performance.mark. This can be used to mark specialized moments in your applications startup, or a specific interaction.
Though you can use
usePerformanceMark with any arbitrary string passed as the
stage parameter, two special values exist with predetermined meanings and extra side-effects. These values are contained in the
Stage.Usable: Marks the page usable using
performance.usable(). A page should be considered
usablewhen the bare-minimum of functionality is present. Usually this just means when the React application has mounted the page, but the application is still waiting on some asynchronous data.
Stage.Complete: Marks the page complete using
performance.finish(). A page should be considered
completewhen all data is fetched and the page is fully rendered.
For page level components, you should always have at least a
usePerformanceMark call with a
In a more complex application with calls for remote data or other asynchronous work which blocks some portion of the tree from rendering it is common to need more granular data about the lifecycle of a page load. To gather this data we can make the
stage parameter we pass to
usePerformanceMark dynamic, and give it
complete only when data has loaded.
A custom hook which takes a url and sends a detailed breakdown of all navigations and events which took place while the page was loading. This hook should generally only be used once at the top of your component tree.
To get sensical data, applications using
usePerformanceReport should be sure to have at least on performance mark on each top level page.
This library also provides the following component implementations of the above hooks:
<NavigationListener />, the component version of
<LifecycleEventListener />, the component version of
<PerformanceReport />, the component version of
<PerformanceMark />, the component version of
The components are provided primarily for backwards compatibility, and the hook implementations should be preferred.
This package also re-exports all of the API from