React Performance & Optimization
Performance in React apps can be thought of in 2 ways:
- Loading Performance - Compressing & loading code/assets (mostly Non-React things)
- Runtime Performance - CPU & rendering issues (mostly React-specific things)
Loading Performance - This is a measure of how fast the content is loaded when a user visits your webpage. Some specific metrics are First Contentful Paint (FCP), Largest Contentful Paint (LCP), First Input Delay (FID), TTI (Time to Interactive), and maybe a "Speed Index".
Runtime Performance - This is a measure of how "smooth" your application runs and functions after the initial load. Some specific metrics for this might be "frame rate", "CPU", and "Memory Usage".
Send as little code/media as possible over the network, and optimize everything
- Utilize GZip compression server-side to compress all in-flight HTTP requests
- Optimize all images and videos included in the bundle
- Lazy load images
- Various Methods of Code Splitting
- Server-Side Rendering (SSR) and Static Site Generation (SSG) can improve the First Contentful Paint and Time to Interactive metrics, as they allow the browser to render the page more quickly (and are accessible by search engines).
- Adding SSR / SSG to an existing project is no small feat. Try to evaluate early on if your application would benefit from these technologies so you can configure your project the right way from the start.
Modern React is pretty fast by default. Unless you're building complex components/features - you don't need to reach for optimization tactics until you notice a component/feature behaving slowly. The best advice to follow:
- Don't optimize too soon
- Know what to measure
In modern React apps, most of the performance issues you'll run into can probably be simplified down to rendering problems (either too slow, or too much). The visual in the tweet below explains how rendering can cause cascading effects across a large application.
Read more from Alex Sidorenko in his series A Visual Guide to React Rendering.
Other helpful resources for understanding how to measure runtime performance in React apps:
- Measuring Performance in Chrome
- Debugging in the Browser
- Devtools Coverage - shows how much of your code is being shipped but then NOT running (while session is recording)
- Use the Profiler to measure rendering performance of a React tree programatically
- How to Detect Slow Renders in React?
- How many re-renders are too many?
Runtime performance issues usually boil down to two types of issues:
Fixing Slow Renders
Fixing Profuse Re-Renders
Memoization & Virtualization can solve slow renders or profuse re-renders in different ways:
- use memo to memoize components and cut down pointless re-renders of child components when their props aren't changing
- use useMemo() to memoize specific work within your components, cutting down the render time by skipping it if the recalculation isn't needed
- use useCallback() to memoize (cache) functions that are created for use in your components, cutting down the render time by skipping it if the function doesn't need to be recreated
- use virtualization for efficiently rendering large lists of datasets, cutting down render time
- A library called Million.js has been making the rounds lately, apparently able to replace the VDOM in React with a version that's up to 70% faster.
- Consider throttling/debouncing frequent updates in the DOM. Humans can't see/process more than a few subtle updates in 1 second (1000 milliseconds), so there's no computational need to update something 100's of times per second.
- Read more about how fast of an interaction is too fast for humans
- For actual changes in the UI/data that's being shown, try not to show more than 4-5 updates per second. Any more, and the humans using your app won't even be able to tell the difference between each update, making it pointless.
- RxJS has traditionally been a great resource for throttling data throughput in a JS application
- Treat loading/pending states that are active for < 400ms uniquely, you should do either:
- Simply don't show the loading state, as "delay" in the app that's < 400ms probably won't be perceivable by the users anyways
- If you show the loading/pending state - require a minimum of ~400ms before switching back to a non-loading state
- Pitfalls of
- Using SVGs in JSX can lead to bloated bundles
- Improving React Interaction Times by 4x
- Improve the Performance of your React Forms
- One Simple Trick to Optimize React Re-Renders
- Making Tanstack Table 1000x faster with a 1 line change
- Optimizing React performance without refs and memo
- React Performance Checklist - Cory House