React Performance – A Definitive Guide to Optimize Major Performance issues in React

Shares

React Performance – A Definitive Guide to Optimize Major Performance issues in React

Hardik Shah
in React
- 19 minutes
React performance

Throughout past 1-2 years, React has matured as an UI library and with that – lots of best development practices have evolved over the years. However, it’s possible to run into performance issues in a large web applications, if you don’t employ best practices and ignore the internal architecture of React.

In this article, I am going to cover some strategies that are extracted from thousands of programmers and agencies who have faced some kind of React performance issues in past.

TL;DR

  • Know how your components interacted – In React, you can sort out about more than half of performance issues by analyzing how your components interacted in your app by using amazing tools such as React dev tools. But before you figure out the components behavior, make sure that you know how React works under the hood.
  • To stay ahead in the game of speed, only load the resources you need upfront – Prioritize the resources which are needed to be loaded first and lazyload the rest of code or resources with techniques such as code-splitting.
  • Audit and Trim out your Javascript bundles to eliminate any code redundancy – There might be higher chances when you are duplicating things more unnecessarily or unintentionally. Make sure to get them analyzed and sort out the size of your bundled code.
  • If you are that much concerned for SEO except Google, then ask yourself twice if Server Side Rendering (SSR) is really the thing you need – Server side rendering is not as fancy as it sounds. If not implemented properly, it can make performance of your app more worse or even disaster I would say.
  • Use RESELECT and Immutable.js if you face performance issues in Redux based React apps

Before I dive into React performance issues, let’s first discuss the internal architecture of React to make things more clear to you in terms of performance.

React Performance: How UI is rendered in React?

In React, your UI is rendered by updating something called as DOM (Document Object Model).

In basic layman terms, DOM resembles like a tree like structure of objects and nodes.

In React, however, HTML is not the root node of a DOM. In Fact, it is only one single node from the Javascript’s prototype chain so that it gets the functions to calculate styles, properties or manipulate the individual nodes.

In simpler terms, DOM in React is a standard that defines how you get, update or manipulate HTML elements.

The React DOM knows how to render a page but it is not so intelligent to keep track of each node and components which are updating.

So, let’s say you have a list of 10 items on your SPA (Single page Application) and you have to update one of them when your user interacts with UI. With DOM, you will end up updating all the components as entire list will get re rendered.

That’s where the concept of Virtual DOM comes into play. The Virtual DOM is React’s local copy of the HTML DOM.

Let’s go back to our list example and figure out how Virtual DOM will let React renders the UI.

When user interacts with UI, React would create a copy in the form of Virtual DOM and updates the UI after comparing the real DOM with virtual DOM. In this way, the item which needs to be updated will update without affecting other items.

React Performance Issues – What’s BAD about Virtual DOM?

In the above section, you read about good parts of Virtual DOM.

Now, let’s be familiar with bad parts of Virtual DOM which are single-handedly responsible for many performance issues in React.

React leverages a diff algorithm in order to minimize the operations to be done at nodes. However, the algorithm in its own sense is not perfect.

For instance, let’s take a look at the GIF given below which resembles a react DOM tree. Let’s say that values of the nodes in yellow has changed, and needs to be updated.

React Virtual DOM

As you can see above, React goes on rendering the whole subtrees instead of rendering the relevant component. When these unnecessary renders are left idle in a React application, they can eat up unnecessary CPU and memory resources which can be fatal for your React application. This is why they are called as Wasted Renders.

When it comes to Virtual-DOM, about half of performance-related issues in React are originated from:

  • Redundant processing in components that do not update the DOM
  • Diff Algorithm keeps on updating leaf nodes that do not need to be updated
  • Heavy CPU computation due to Diff algorithm updating components

The entire remaining sections blog post goes deep into the common performance based issues in React web app and the best practices to optimize React app performance.

Un-necessary Renders in React Application

As already explained above, React Virtual DOM concept might be amazing but it knowingly or unknowingly creates many performance issues by unnecessary rendering of components in DOM tree. This even makes a medium-sized app slow in terms of performance.

For instance, consider following GIF which is extracted from a Todo web app:

wasted renders react example

Note that while entering the text of second todo row, the first todo row keeps on flashing on the UI with every keystroke. This means it is being re-rendered by React together with the input. These unnecessary renders are sometimes referred as “wasted” renders. We know it is unnecessary because the first todo row has not changed, but React doesn’t know this.

Now Imagine what will be the condition when you have a big and complex app in which states and newer updates are flying everywhere?

It will be a serious performance throttle to your React application. Right??

Detecting wasted Renders in a React Application using React dev tools

Like most cases, it’s a developer job to identify who all are wasted renders responsible for making your application slow.

But, how can you detect wasted renders in a React application?

This is where React performance tools chrome extension would comes handy for your application. At present, these tools only work blazingly fast with React version 16.

You can start analyzing your profile by keeping a check on User timing accordion which will show you all the logs related to heavy CPU usage. This way you can identify which parts of your app are slow on rendering.

[Note: you can learn more about these tools on Google Chrome Devtools official documentation. ]

Attached below is a GIF which illustrates discovering of slower components in a React web app using React devtools:

Discovering slower components React devtools

Detecting unnecessary rendering of components by using “Why did you update” library

If you are still not satisfied by the output of React performance tools, you can still use “why-did-you-update” library that hooks into React and detects potentially unnecessary component renders. The library once goes in an active stage will shout out into your console anytime a piece of state makes your component update which in reality shouldn’t update at all.

[Note: “Why-did-you-update” will comes handy in local development but make sure it’s disabled in production since it will further slow down your app.]

Increasing React performance by fixing unnecessary rendering of components in React

In React, whenever a component’s state changes, both component and its children gets rendered unnecessarily. Often times, the component and children’s barely need to change yet we end up rendering everything.

This seems inefficient. Right?? Let’s have a look on what it takes to optimize the un-necessary rendering in React.

Implementing shouldComponentUpdate for preventing unnecessary rendering

Most performance related issues in mobile apps tend to be sorted out by implementing smaller changes, but these changes are enough capable to make your app faster as the end result.

One such change which seems smaller but is capable of providing enough measurable and perceivable performance improvement to your React app is shouldComponentUpdate method.

As the syntax implies, shouldComponentUpdate is a react lifecycle hook using which you can instruct React to avoid re-rendering a given component if its state don’t change.

Let’s say you have a list composed of many components with a complicated nested structure, and one item in the list changes. It would be a waste of resources for your app to render the un-necessary components from the list. By implementing shouldComponentUpdate, you can basically tell React to ignore all the components except the one that changed.

Twitter engineers experienced 10 times performance improvement in their Progressive Web App by correct implementation of shouldComponentUpdate method.

Have a look at the GIF given below:

wasted renderers in Twiiter lite app

Here the entire conversation box is also rendering when you click on heart icon for liking the tweet. This is a good example of wasted renderers in React. Implementing shouldComponentUpdate in this case provide huge relief to Twitter engineering team as it prevents the entire component tree from updating and processing about more than one-tenth of a second.

But, if the Twitter succeeds and getting faster speed with React, it doesn’t mean shouldComponentUpdate can work in your case too.

So, how can you identify when should you use shouldComponentUpdate in your React app?

Well, the answer is simple… If you experienced any significant performance improvement from it, it is the required change for your app.

If it doesn’t then it will not do anything except slowing down your React components. Maintaining your code with shouldComponentUpdate is hard due to which it could be also major source of bugs.

Improving performance due to unnecessary renders using PureComponent

If you are not experiencing any performance gain using shouldComponentUpdate then you might also try using Pure Component which is the more performant version of React’s component class. The usage of Pure Component gives a considerable increase in performance because it reduces the number of render operation in the application.

Pure component is exactly similar to Component class in React except the changes that it automatically handles shouldComponentUpdate method for you.

Have a look at below GIF:

Pure component example

Note that everytime the input text is typed, every single text component is being re-rendered. This is where React.PureComponent class will come handy. By implementing Pure component, we can eliminate the re-rendering of component.

Preventing wasted Renders in React with Immutable.js

While defining shouldComponentUpdate method, it is your job to tell React if component needs to be re rendered to reflect state and new properties. Otherwise, React would leverage a diff algorithm for this decision which is computationally expensive.

An ideal way to use shouldComponentUpdate is to make it more lightweight as possible since it is called for every single component.

Recurrent calls to this method could result in a serious performance throttle in your React application.

Moreover, in case your components are re-rendered which resulted into change in properties, at some point you’ll want to do comparison with the new and old props. However, the comparison like this is not straightforward.

This is where immutable data comes into picture. Immutable data structures are not changeable or you can say they are not capable of changing.

Immutable data is the answer to this problem. By definition, immutable data structures never change. Immutable data allows you to compare direct object references instead of doing deep-tree comparisons. This makes a React app faster.

Optimizing loading time for big PWA React apps

Over the time, while writing code in React, you end up making wrong async calls which in turn increases the bundle size of a React based progressive web application. The increased bundle size in turn slower down the loading time of the React application.

However, there are some tools and tips by which can optimize the loading time by optimizing the build size of the application. Let’s dive in!

Identifying problematic bundles for a React based PWA

The first step in optimizing the bundle size of React app deals with identifying the problematic chunks of code in your production build.

If you are using webpack for easy bundling in React, then you can use Webpack Bundle Analyzer plugin for analyzing your build dependencies.

While analyzing dependencies from the plugin , you could come across something like this:

Webpack bundle analyzer GIF

In above GIF, you can see a lot of purple, blue and pink blocks as an output for a React based PWA build. You can identify problematic chunks of code from these blocks.

Pinterest were able to identify tons of duplicate code in their build with the help of Webpack bundle analyzer plugin. They moved duplicate code from the async chunk to their main chunk which decreased the size of all lazy loaded chunks by 90 %.

Mentioned below are some more excellent Webpack plugins which might help you to evaluate problematic chunks of code from your build:

[Note: Before using the above mentioned plugins, make sure to evaluate them so that you might aware of the use case beforehand]

Optimizing the Javascript bundle with Gzip compression

For further optimizing your app’s loading time, you can use Gzip compression module which will further reduce your bundle size to a 20% to 25%.

Optimizing the app loading time further with Code splitting

If you want to load a webpage faster for your users then it makes a lot of sense for you to only load the code which your user needs upfront.

Especially in case of single page apps (SPA), there is no particular reason to load the code that comprises an admin panel when you want your user to view a landing page.

Makes sense, Right??

This is where Code splitting comes into play. Code-splitting your app can help you “lazy-load” just the things that are currently needed by the user, which can dramatically improve the performance of your app.

By utilizing code-splitting technique which breaks the Javascript bundles into three different categories, Pinterest take down the size of their bundles from 650KB to 150KB.

Pinterest page load time

Not only Codesplitting reduced bundle size in the Pinterest’s case but it also results in improving their app loading time from 23 seconds to 6.5 seconds.

If you are using webpack, you can split your code into multiple files by using CommonsChunkPlugin.

If you are not fan of using webpack, then you can use ReactLoadable library for splitting down your bundled code for lazy-loading.

Optimizing the app loading time for SEO – Isomorphic React

React is an incredible framework for building Single Page Application (SPA). However, with it being a client-side-framework, we might experience following performance issues while building an application:

  • Bad SEO: Since the javascript-related content is rendered client-side, search engine like yahoo, baidu and Bing won’t be able to index them as all they could see a blank page which relates to the negative SEO.
  • Slow app loading time: When the application loads initially, there is no cache of JavaScript in the browser. If the application is big, the time taken to initially load the application will also be huge.

[Note: Google bots are now capable of rendering Javascript in certain situations. That means, if you are not worried about SEO for other search engines such as yahoo, bing, baidu, duckduckgo etc., you will be alright with your SPA without optimizing anything]

However, If you are really concerned about the SEO for search engines other than Google then it makes a lot of sense for you to go with Server side rendering and build an Isomorphic React app.

In server-side-rendering, the JavaScript-related content is rendered from the server initially. After the initial rendering takes place, the client-side script takes over and it works like a normal SPA.

SSR helped the engineering team at Housing.com serve the meaningful content to users 3 seconds faster on normal 3G networks as compared to client-side rendered app.

Below, you can see a comparison from WebPageTest of Housing.com server side rendered app & a previous version, which used client side rendering.

Server side rendering at Housing.com

But implementing SSR in React comes with lot of code-complexity and cost of setting it up is huge considering the requirement of good servers such as Node.js.

However, if you really want to improve your app’s loading speed, then you can implement SSR with Next.js which will save you a lot of time that goes into setting up the servers for SSR.

Improving the app’s loading time by lazy loading Images

In previous sections, I talked about loading only the necessary code upfront which greatly improves app load speed.

But, what about Images?

Images are the heaviest resources that your user may never see until a web page is fully loaded. They might degrade React app performance due to following reasons:

  • Data Wastage: Loading Images un-necessarily leads to heavy usage of data which is complete waste considering that your user can’t see them properly.
  • Unnecessary consumption of device resources and battery: After an Image gets downloaded from the server, the browser must decode it and render the content to UI which requires heavy consumption of device resources and battery.

In order to improve app-loading time, a rule of thumb is to load the images at the moment of need instead of loading them while the app’s page is loading. This is referred to as Lazy loading of Images.

Pinterest used a progressive lazy loading technique for images in their Progressive Web App.

Pinterest used a placeholder initially at the time of Page load. The image appear as blurr at the initial load but it loads completely once the page is fully loaded.

Progressive Image Loading at Pinterest

Lazy Loading of Images in a React app can be implemented by using React Lazyload library.

Rendering a big list of data

Lists and Grid Views are common pattern to show data in React app. When you have a big list (say 1000+ items) in React which is supposed to render elements on scrolling, you might experience that your UI might lag a bit after you scroll down fast till 1000 items.

Have a look at below GIF:

List Rendering in React

Here scrolling up works great, but when you start scrolling down, everything goes Topsy turvy.

Let’s dig into the details as in what happens under the hood of React when we try to render such a big list of components.

When you scroll down fast, you are doing too much of rendering between components which makes the UI sluggish.

Let me break it into simpler terms for you to understand it easier:

Ever experienced boredom when your teacher makes your lecture long and explained too much of stuffs at a time?

This is because during long sessions, hundreds of messages in the form of information gets exchanged between tutor and students due to which students (say components in the example) feels it hard to consume. As a result they gets bored (which means components gets re-rendered) and tutor starts complaining (which means UI gets sluggish).

Optimizing list rendering with React Virtualized List

Here, Virtual rendering comes into play. Instead of rendering the whole bunch of components, it lets you render just 30 components. Or 40, or 10. Whatever the number, at the end of the day you will render a small subset of the components. This gives you a big performance boost.

React Virtualized list is a library that takes virtual rendering technique into account. Whenever, you feel your UI is lagging due to large list of data, you can use React Virtualized list to make it performant.

Optimizing React list performance by using correct keys for components

React uses the Key attribute to decide which elements can be reused for the next rendering phase. These keys are important for updating elements in dynamic lists.

React does this updation by comparing the keys of the new element with the keys of the previous element. In this process, it might also render components having a new key and unrender the components which have keys that are not used anymore.

I have seen that most developers wrongly assumes index of an item as the key while rendering a list which is why they ran into performance issues such as:

  • Unnecessary re-renders in list
  • Unwanted bugs which may make an UI unresponsive while scrolling

Have a look at the following GIF:

Here, Clicking on “Add Item” will reflects the text of previous item instead of allowing the user to type the third item. This is a great example of using index of items in place of component keys which results in re rendering of the previous component in third item case.

Using the key attributes in your list will help you out maintaining the consistent performance with your lists. As a side note, do remember that your key value should be unique for every single component in the list.

Henceforth, use Key={ } wisely while implementing list in React.

Optimizing performance issues in Redux-based React apps

When used together, React and Redux are an awesome combination of technologies that help us structure complex applications with true separation of concerns.

When you are using Redux, your app’s components which have subscribed to Redux store gets re-rendered unnecessarily which slows down your app with major fractions of time.

Engineering team at Yahoo had faced similar performance issue. Their Mainpage component which was located at the higher order of the tree was re-rendered due to which they were experiencing their UI to be clogged while rendering huge number of data rows.

In Yahoo’s case, they still faced the performance lag in their Redux-based React app even after optimizing the component rendering with shouldComponentUpdate.

 Optimizing Redux based React apps with RESELECT

RESELECT is a library which should be used in your Redux based React apps when you have your higher order components handling the complex rendering operations.

Yahoo experienced a significant improvement in performance of their Data Analytics tool after using RESELECT.

Using Immutable.js to further optimize performance issues in Redux based React apps

I talked about the importance of Immutability in a previous section. There is even a benchmarking study which demonstrates that using Immutable.js in Redux-react app can improve application performance in considerable manner.

The study compares two large-lists (10,000 items each) in which one is mutable and other is immutable. When the performance of both the lists were analyzed, the immutable list was said to be 4x performant than non-immutable one.

Here’s the result:

Performance benchmark Mutable vs Immutable data with Redux

Using mutable data structures could results in unnecessary data copying due to a nested Redux state tree which consumes lot of device memory and slows down the React app performance.

On the other hand, Immutating data structures in Redux based React app ensures that all operations of app returns a newer version of data structure and keep the original data structure intact instead of updating it. This considerably boost the performance of your Redux based React app as you are not copying the data anymore.

Conclusion

Hopefully I have guided you to optimize your React performance issues with this handy yet long guide. If you enjoyed this post, you might like to follow me on Twitter.

Still facing issues building your React app? Shoot out an email to me at hardik@simform.com. I’d be happy to help you. 🙂

Hardik Shah

Working from last 8 years into consumer and enterprise mobility, Hardik leads large scale mobility programs covering platforms, solutions, governance, standardization and best practices.

ReactJS

Building High Performant React Progressive Web App

Subscribe and get our Exclusive 10 Points Checklist to build High Performant PWAs in React

You have Successfully Subscribed!