React Compiler — React's new build tool
React Compiler is a new build tool designed to automatically optimize React apps. We take a look at what this means and what problems it solves for building large-scale React applications.
At React Conf 2024, the React team unveiled React Compiler—a new build tool designed to automatically optimize React applications. In today's issue, we'll delve into what this new compiler entails and explore how it aims to change the way we optimize and manage React code in the future.
In the “Building Large Scale Apps” newsletter, we share content on building and maintaining large-scale web applications, with a focus on front-end engineering and JavaScript technologies.
Some content we share will focus more on exploring code changes and API enhancements, while other content will dive into sharing experiences, case studies, or insightful anecdotes from seasoned engineers in the industry working with large-scale web apps.
To get issues like this in your inbox, be sure to subscribe!
React Compiler is an experimental compiler introduced by the React team aimed at significantly improving the performance of React applications by automating the optimization process. It optimizes React applications while allowing developers to maintain their existing React codebase without the need for extensive rewrites.
I [Addy] spoke about React Compiler and a few other updates to React and other JavaScript frameworks at Google I/O. If you’re interested in watching the recording, check out the talk here—Navigating the JavaScript framework ecosystem!
Manual Memoization
React enables developers to encapsulate UI logic and state management within individual, reusable components. Each component in React has a lifecycle, and during this lifecycle, it may re-render multiple times due to changes in state or props. If not managed carefully, these re-renders can become a significant performance bottleneck, particularly when components frequently update due to complex state dependencies, or when they are nested within large application structures where minor data changes trigger extensive re-render cycles across the component tree.
Consider the following pseudocode for this simple TodoList
component:
The TodoList
component above handles todos and their display, controlled by state and props. The handleChange()
function updates the todo state by calling a getUpdated()
method when a todo item is interacted with. The visibility filter is applied through the getFiltered()
method, determining which todos are to be displayed.
The above component example is simple, however, as a React application scales and begins to manage large datasets or perform expensive computations, the default rendering behavior of components can sometimes lead to performance bottlenecks. React’s rendering model re-renders the entire component subtree whenever state changes, which is efficient for small components and datasets but can become costly with larger datasets or complex UIs.
To overcome this performance bottleneck, React has introduced capabilities for manual memoization over the years, such as React.memo, useMemo, and useCallback.
Memoization is an optimization technique used to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.
Continuing with the TodoList
example above, we could use the React.memo()
function to ensure that the child Todo
components re-render only when their props change. Given that todos are likely to be added or modified frequently, this approach prevents unnecessary re-renders of child Todo
components that have not changed.
The handleChange()
function in the component can be wrapped in the useCallback()
hook to ensure it is not recreated with every render.
Finally, by using the useMemo()
hook, we can ensure that the filtered
list is recalculated only when the values of todos
or visibility
change. This approach prevents the filtered list from being recalculated on every render, which is advantageous if the getFiltered()
calculation is costly or the list is extensive.
By applying the above optimizations, the TodoList
component can handle larger amounts of data more efficiently by reducing the number of re-renders and enhancing overall performance.
Automatic Memoization
React.memo
, useMemo
, and useCallback
, while effective, require us to manually determine what to memoize and when. This manual process is not only time-consuming but also prone to mistakes, leading to either over-optimization or insufficient memoization, both of which can degrade performance.
This is where React Compiler steps in. React Compiler is a new build tool that automatically optimizes a React app. It works with plain JavaScript and understands the Rules of React, so we don’t need to rewrite any code to use it. The React team has been testing it out in Meta, and it’s already powering a few production surfaces, like Instagram.
The React Compiler intelligently applies memoization across the components and Hooks of a React application. As we discussed earlier, React can sometimes do more work than needed when an entire subtree needs to be re-rendered, even though not much has changed in the UI. React Compiler can figure out exactly what’s needed to prevent cascading renders and compile fine-grained memoization versions of your code to stop that.
What does this mean for us? This means we can write idiomatic React code and let the Compiler handle the optimizations for us 🤯.
Getting started
While the React Compiler is currently in use in production at Meta, it’s important to emphasize that it’s still in the experimental stage and not yet deemed stable for broad adoption.
For those keen to explore React Compiler before its official release, the React team encourages a few steps to take:
Prior to installing the compiler, check the compatibility of a React app with the npx react-compiler-healthcheck@latest
command. This script evaluates various aspects of a React project to determine how well it can integrate with the new React Compiler.
Install eslint-plugin-react-compiler
Install the eslint-plugin-react-compiler ESLint plugin to better identify code in a React app that violates the Rules of React. This will indicate how much of the code in the app will be skipped by the compiler and not optimized until the issues are resolved.
Roll out the compiler to the codebase
For existing codebases, the React team recommends rolling out the compiler to a small subset of directories before gradually expanding coverage to the whole app. This is to minimize the impact of any potential issues arising from false negatives where the compiler might incorrectly handle components or hooks that do not strictly adhere to the Rules of React.
We’re currently hard at work updating “Building Large Scale Web Apps” with bonus content to cover all the new changes coming into the React ecosystem. We’ll have more details to share on these updates in the next few weeks!
If you’ve purchased the “Building Large Scale Web Apps” book and are greatly enjoying the book, we'd be incredibly grateful if you could leave a positive review! Your feedback helps us reach more developers and continue creating high-quality content like this.
For more comprehensive guides on getting started with the experimental React Compiler, check out the documentation produced by the React team below:
Teams interested in integrating React Compiler into their production React applications can also directly engage with the React team by applying to join the React Compiler Working Group. Finally, for more information on using the Compiler, check out these great talks given by the core React team members at React Conf 2024:
See you soon!
Your friends, Addy & Hassan
Enjoyed this issue? Be sure to subscribe to get this newsletter directly in your inbox.