Let’s look at a popular online retailer: Nike.com. With outdoor exercise increasing in popularity post-COVID, Nike’s running shoe offerings are compelling (arguably too compelling) and we want to buy some.
But the experience on mobile when we navigate to a product details page for a VaporFly shoe is slow and janky.
There’s a few obvious problems:
- The page takes a long time to load. Subjective to be sure, but it’s over 5 seconds.
- There are multiple asynchronous parts rendering at different times causing contentful paint events. (The free shipping banner, the “Sign in to Buy” button etc.)
- There are several layout shifts occurring making the page load look janky. Particularly jarring is the product image render ing below the title and then jumping above it.
We can see that the page “feels” slow, but can we quantify it? Can we determine which specific pieces are responsible for the slowness?
Chrome has great performance diagnostic tools. We’ll start on the Network tab of the developer tools.
This gives us some quantitative metrics:
- The initial document took 143ms to return from the server.
- 283 total requests to load the page.
- 5.7MB transferred over the wire.
- 12.9MB of uncompressed resources.
- 5.6 seconds to finish.
283 requests is unexpectedly high, and overboard for a product details page (or really, anything). That number of requests is going to put a floor on how fast a site can be, regardless of any other improvements.
Many of the requests are served over HTTP/2, which does help with multiplexing, but it would still be better for performance to make dramatically fewer calls.
We can investigate further by filtering the Network tab to specific resource types. This let’s us see the size and number of requests for each type (JS, CSS etc).
Creating a performance profile gives us a snapshot of a specific page load. We get detailed flame graphs and timelines of what’s happening. If you want to play along, download the profile used in this tutorial, and load it in to Chrome to take a look.
There is a lot of information displayed in our Chrome performance profile. It can look daunting at first. Let’s break it down and go through each section individually.
The top section ( 1 ) is where frames per second, network activity and CPU utilization are graphed over time.
The red “L” is the
OnLoad event firing.
Largest Contentful Paint (LCP) and
First Contentful Paint (FCP) are shown as well. We also get more detailed information on just how long some tasks were taking. These longer tasks are good places to start when investigating why the script execution is taking so long.
The bottom section of the profile ( 3 ) shows summary and list data.
A good place to start when investigating script issues is the “Bottom Up” tab. This gives us a more granular view of the pie chart from the Summary tab. We can see similar time taken percentages here, but now we can expand the arrows and dive deeper.
From the file names, it’s clear that a large portion of our time is spent inside React. Given the number of renders and the high CPU/low FPS, it’s likely the application is in significant need of optimization!
We can see that
setState is prominently represented in the list, and repeated calls to
setState will often cause layout thrashing and other poor performance issues. In terms of code improvement, drilling in to these React functions would be a good place to start!
We know from the graphs that script execution is pegging the CPU and frames per second is suffering. We also know that there are too many renders occurring throughout the page lifecycle. Coupled with the amount of time spent in React, we can conclude that the React application is a large part of the problem! Spending time performance tuning the React application would help address the layout shifts and numerous contentful paints.
No wonder there are 87 requests and 8.9MB of script! There are numerous third party analytics and marketing products represented here, along with evidence of the internal corporate structure of Nike’s software teams. In short, this list is too long. Each of these scripts has a cost in terms of performance, and also maintenance and user experience/quality.
If Nike wanted to improve their site’s performance they would do the following:
- Optimize their React app. Ensure judicious use of
setState. The amount of jank/layout shift on page load, coupled with the pegged CPU, strongly suggests that there are ample opportunities to batch state updates or do less work.
- Remove some of their analytics and tracking third parties. There’s too many cooks in the kitchen here and each one could be contributing to poor page experience.
- Consolidate some of their first-party scripts. There’s dozens of scripts loaded from all over nike.com. It’s Conway’s Law in action.
Making these changes would be a big start towards improving performance on Nike.com!