While React offers many advantages, optimizing the performance of React applications can be tricky. In this post, we will cover nine techniques for boosting the speed of your React applications.
Avoid redundant renders
Redundant renders are a typical problem in React that can quickly worsen the performance of your application.
In my experience, pragmatically planning the flow of the state helps avoid redundant renders most of the time. Here are some things to keep in mind when writing components that use the application state.
Use derived state whenever you can
Avoid using the state to store the data that you can derive on the fly instead. Doing so simplifies syncing your data since derived variables will automatically recalculate whenever their source changes.
If you would like to learn more about this tip, check out my post on deriving state in React .
Be selective about what your components get to decide
Keep in mind the information you provide to your components. Each piece of the state you pass to them could potentially cause re-renders. So make sure to pass only the information your components need to function. Periodically clean up or refactor props that you’re not using.
Pass flattened props and primitives
Instead of providing whole objects as props, try passing flattened and simplified versions. Providing complex, deeply nested objects as props could cause unexpected side-effects and renders for your components.
Using flattened objects and primitives as props simplifies optimizing the performance of your components in the future.
Use memo, useMemo, and useCallback
useCallback are React utility functions for memoizing components and reducing the number of renders. These three functions are the main tools for optimizing the performance in React.
But be mindful of how and where you apply them in your code. Sometimes, there is nothing to be gained from applying these functions. For example, if your re-renders are caused by a frequently updating context, wrapping your component in
memo will not help reduce the number of re-renders.
Check out this post if you would like to learn more about optimizing the number of redundant renders in your React components .
Conditionally render components instead of hiding them with CSS
This is a mistake I most often see new React developers make. If they have a use case where they need to hide components from the user, they use CSS props, like
visibility for that purpose.
However, doing so is suboptimal in terms of performance. The components are still present in the DOM even if not visible to the user. Instead, use conditional logic to avoid rendering the component altogether.
Use React Profiler
React Profiler is a performance tool that lets you collect timing information about your components.
You can use React Profiler by installing
React Dev tools
The Profiler will show you how often each component in your app renders and how long each render takes.
You can then use this information to optimize performance by memoizing expensive components or avoiding unnecessary renders altogether.
To learn more about React Profiler, check out this post from React docs .
Local state vs Redux
Redux is a powerful library for managing the state of your application. However, be careful not to overuse it. Redux is great for truly global data, like user session or app theme.
While Redux does a few things to optimize its performance, it will still take a toll on your app if you use it for frequent events, like tracking user typing.
A global state will always be slower than a local state, so try using the local state whenever you can. The prop drilling problem that comes with using local state can often be solved with component composition. If you’re interested, here’s a post where I cover component composition for React in more detail.
The optimistic response is when you don’t wait for the API response to propagate changes to your UI. It’s one of the best ways to increase the perceived performance of your app.
The main idea is to make an async request and, at the same time, update your local state data as if you have already received the response.
Implementing this approach will often add complexity to your state management. So first, consider whether the trade-offs between complexity and performance are worth it.
Some API libraries provide optimistic response features, for example, Apollo . Apollo is a GraphQL client with built-in state management and caching capabilities.
Web Worker API allows running computationally expensive operations separately from the main thread.
Instead, you could run your function using a web worker.
A great example could be processing a media file. Usually, you want to apply some processing to the image before saving it. Perhaps you want to reduce the size of user avatars before uploading them to your cloud storage to save on computing costs. Powerful image manipulation libraries like sharp can often freeze up your main execution thread, so it’s better to run them in a separate web worker.
Virtual lists are particularly useful when working with a large amount of tabled data.
The idea behind the virtual lists is to render only items that need to be within the view of the screen. Think of it as a just-in-time rendering:
Even if your lists contain hundreds of items, the app will only render ten at a time, which will appear and disappear out of the view as the user scrolls.
react-virtualized is a popular option for implementing virtual lists in React.
The dynamic import is how you would add code-splitting into your app. Code splitting is a performance optimization technique for loading pieces of code only when needed. The code-splitting is handled by bundlers, like Webpack or Browserify.
Here’s what the basic syntax of dynamic import looks like:
When Webpack encounters this syntax, it will automatically start code-splitting your app.
You can use the dynamic import syntax to split your React components into separate bundles. This way, Webpack will only load the components needed to render in the current view.
React.lazy also provides a React-specific way to code split your app. It allows loading React components lazily on-demand and suspends rendering components until they are ready to be rendered.
Deciding where in your app to introduce code splitting can be tricky. You want to choose places that will split bundles evenly but won’t disrupt the user experience.
A good place to start is with routes. People are used to a wait time between pages. Splitting your code by routes will help keep page-load times down without adversely affecting the user experience.
To learn more about code splitting and dynamic imports, check out this post on code-splitting from React docs .
React fragments let you group a list of children without adding extra nodes to the DOM. React requires each component to have one root node. A lot of people default to using divs for this purpose, but that’s not always the best option. A large number of divs can hinder the performance of your app.
Fragments let you circumvent this issue by allowing you to group multiple React nodes without adding extra nodes to the DOM. React merges fragments into the parent DOM during the rendering process.
React performance optimization can be tricky, but following these simple techniques will help you boost application speed.
If you’d like to get more web development, React and TypeScript tips consider
following me on Twitter
where I share things as I learn them.