Skip to content

Commit b877eb9

Browse files
committed
Update react-compiler docs
This upstreams some of the [introduction post](reactwg/react-compiler#5) into the docs since this knowledge is quite fundamental to understanding the compiler, and it's confusing to not have it linked in the canonical source for the docs. It also adds more information about [debugging](reactwg/react-compiler#7) and reorganizes a few sections to make things clearer ghstack-source-id: 78344ec92057c8d93f22cace367876fcbd7346ff Pull Request resolved: #6895
1 parent 80d30d7 commit b877eb9

File tree

1 file changed

+142
-51
lines changed

1 file changed

+142
-51
lines changed

src/content/learn/react-compiler.md

+142-51
Original file line numberDiff line numberDiff line change
@@ -30,58 +30,91 @@ The compiler also includes an [eslint plugin](#installing-eslint-plugin-react-co
3030

3131
### What does the compiler do? {/*what-does-the-compiler-do*/}
3232

33-
The compiler understands your code at a deep level through its understanding of plain JavaScript semantics and the [Rules of React](/reference/rules). This allows it to add automatic optimizations to your code.
33+
In order to optimize applications, React Compiler automatically memoizes your code. You may be familiar today with memoization through APIs such as `useMemo`, `useCallback`, and `React.memo`. With these APIs you can tell React that certain parts of your application don't need to recompute if their inputs haven't changed, reducing work on updates. While powerful, it's easy to forget to apply memoization or apply them incorrectly. This can lead to inefficient updates as React has to check parts of your UI that don't have any _meaningful_ changes.
3434

35-
You may be familiar today with manual memoization through [`useMemo`](/reference/react/useMemo), [`useCallback`](/reference/react/useCallback), and [`React.memo`](/reference/react/memo). The compiler can automatically do this for you, if your code follows the [Rules of React](/reference/rules). If it detects breakages of the rules, it will automatically skip over just those components or hooks, and continue safely compiling other code.
35+
The compiler uses its knowledge of JavaScript and React's rules to automatically memoize values or groups of values within your components and hooks. If it detects breakages of the rules, it will automatically skip over just those components or hooks, and continue safely compiling other code.
3636

37-
If your codebase is already very well memoized, you might not expect to see major performance improvements with the compiler. However, in practice memoizing the correct dependencies that cause performance issues is tricky to get right by hand.
37+
If your codebase is already very well-memoized, you might not expect to see major performance improvements with the compiler. However, in practice memoizing the correct dependencies that cause performance issues is tricky to get right by hand.
3838

39-
### Should I try out the compiler? {/*should-i-try-out-the-compiler*/}
40-
41-
Please note that the compiler is still experimental and has many rough edges. While it has been used in production at companies like Meta, rolling out the compiler to production for your app will depend on the health of your codebase and how well you've followed the [Rules of React](/reference/rules).
39+
<DeepDive>
40+
#### What kind of memoization does React Compiler add? {/*what-kind-of-memoization-does-react-compiler-add*/}
4241

43-
**You don't have to rush into using the compiler now. It's okay to wait until it reaches a stable release before adopting it.** However, we do appreciate trying it out in small experiments in your apps so that you can [provide feedback](#reporting-issues) to us to help make the compiler better.
42+
The initial release of React Compiler is primarily focused on **improving update performance** (re-rendering existing components), so it focuses on these two use cases:
4443

45-
## Getting Started {/*getting-started*/}
44+
1. **Skipping cascading re-rendering of components**
45+
* Re-rendering `<Parent />` causes many components in its component tree to re-render, even though only `<Parent />` has changed
46+
1. **Skipping expensive calculations from outside of React**
47+
* For example, calling `expensivelyProcessAReallyLargeArrayOfObjects()` inside of your component or hook that needs that data
4648

47-
In addition to these docs, we recommend checking the [React Compiler Working Group](https://github.com/reactwg/react-compiler) for additional information and discussion about the compiler.
49+
#### Optimizing Re-renders {/*optimizing-re-renders*/}
4850

49-
### Rolling out the compiler to your codebase {/*using-the-compiler-effectively*/}
51+
React lets you express your UI as a function of their current state (more concretely: their props, state, and context). In its current implementation, when a component's state changes, React will re-render that component _and all of its children_ — unless you have applied some form of manual memoization with `useMemo()`, `useCallback()`, or `React.memo()`. For example, in the following example, `<MessageButton>` will re-render whenever `<FriendList>`'s state changes:
5052

51-
#### Existing projects {/*existing-projects*/}
52-
The compiler is designed to compile functional components and hooks that follow the [Rules of React](/reference/rules). It can also handle code that breaks those rules by bailing out (skipping over) those components or hooks. However, due to the flexible nature of JavaScript, the compiler cannot catch every possible violation and may compile with false negatives: that is, the compiler may accidentally compile a component/hook that breaks the Rules of React which can lead to undefined behavior.
53+
```javascript
54+
function FriendList({ friends }) {
55+
const onlineCount = useFriendOnlineCount();
56+
if (friends.length === 0) {
57+
return <NoFriends />;
58+
}
59+
return (
60+
<div>
61+
<span>{onlineCount} online</span>
62+
{friends.map((friend) => (
63+
<FriendListCard key={friend.id} friend={friend} />
64+
))}
65+
<MessageButton />
66+
</div>
67+
);
68+
}
69+
```
70+
[_See this example in the React Compiler Playground_](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAMygOzgFwJYSYAEAYjHgpgCYAyeYOAFMEWuZVWEQL4CURwADrEicQgyKEANnkwIAwtEw4iAXiJQwCMhWoB5TDLmKsTXgG5hRInjRFGbXZwB0UygHMcACzWr1ABn4hEWsYBBxYYgAeADkIHQ4uAHoAPksRbisiMIiYYkYs6yiqPAA3FMLrIiiwAAcAQ0wU4GlZBSUcbklDNqikusaKkKrgR0TnAFt62sYHdmp+VRT7SqrqhOo6Bnl6mCoiAGsEAE9VUfmqZzwqLrHqM7ubolTVol5eTOGigFkEMDB6u4EAAhKA4HCEZ5DNZ9ErlLIWYTcEDcIA)
5371

54-
For this reason, to adopt the compiler successfully on existing projects, we recommend running it on a small directory in your product code first. You can do this by configuring the compiler to only run on a specific set of directories:
72+
React Compiler automatically applies the equivalent of manual memoization, ensuring that only the relevant parts of an app re-render as state changes, which is sometimes referred to as "fine-grained reactivity". In the above example, React Compiler determines that the return value of `<FriendListCard />` can be reused even as `friends` changes, and can avoid recreating this JSX _and_ avoid re-rendering `<MessageButton>` as the count changes.
5573

56-
```js {3}
57-
const ReactCompilerConfig = {
58-
sources: (filename) => {
59-
return filename.indexOf('src/path/to/dir') !== -1;
60-
},
61-
};
62-
```
74+
#### Expensive calculations also get memoized {/*expensive-calculations-also-get-memoized*/}
6375

64-
In rare cases, you can also configure the compiler to run in "opt-in" mode using the `compilationMode: "annotation"` option. This makes it so the compiler will only compile components and hooks annotated with a `"use memo"` directive. Please note that the `annotation` mode is a temporary one to aid early adopters, and that we don't intend for the `"use memo"` directive to be used for the long term.
76+
The compiler can also automatically memoize for expensive calculations used during rendering:
6577

66-
```js {2,7}
67-
const ReactCompilerConfig = {
68-
compilationMode: "annotation",
69-
};
78+
```js
79+
// **Not** memoized by React Compiler, since this is not a component or hook
80+
function expensivelyProcessAReallyLargeArrayOfObjects() { /* ... */ }
7081

71-
// src/app.jsx
72-
export default function App() {
73-
"use memo";
82+
// Memoized by React Compiler since this is a component
83+
function TableContainer({ items }) {
84+
// This function call would be memoized:
85+
const data = expensivelyProcessAReallyLargeArrayOfObjects(items);
7486
// ...
7587
}
7688
```
89+
[_See this example in the React Compiler Playground_](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAejQAgFTYHIQAuumAtgqRAJYBeCAJpgEYCemASggIZyGYDCEUgAcqAGwQwANJjBUAdokyEAFlTCZ1meUUxdMcIcIjyE8vhBiYVECAGsAOvIBmURYSonMCAB7CzcgBuCGIsAAowEIhgYACCnFxioQAyXDAA5gixMDBcLADyzvlMAFYIvGAAFACUmMCYaNiYAHStOFgAvk5OGJgAshTUdIysHNy8AkbikrIKSqpaWvqGIiZmhE6u7p7ymAAqXEwSguZcCpKV9VSEFBodtcBOmAYmYHz0XIT6ALzefgFUYKhCJRBAxeLcJIsVIZLI5PKFYplCqVa63aoAbm6u0wMAQhFguwAPPRAQA+YAfL4dIloUmBMlODogDpAA)
7790

78-
When you have more confidence with rolling out the compiler, you can expand coverage to other directories as well and slowly roll it out to your whole app.
91+
However, if `expensivelyProcessAReallyLargeArrayOfObjects` is truly an expensive function, you may want to consider implementing its own memoization outside of React, because:
7992

80-
#### New projects {/*new-projects*/}
93+
- React Compiler only memoizes React components and hooks, not every function
94+
- React Compiler's memoization is not shared across multiple components or hooks
8195

82-
If you're starting a new project, you can enable the compiler on your entire codebase, which is the default behavior.
96+
So if `expensivelyProcessAReallyLargeArrayOfObjects` was used in many different components, even if the same exact items were passed down, that expensive calculation would be run repeatedly. We recommend [profiling](https://react.dev/reference/react/useMemo#how-to-tell-if-a-calculation-is-expensive) first to see if it really is that expensive before making code more complicated.
97+
</DeepDive>
98+
99+
### What does the compiler assume? {/*what-does-the-compiler-assume*/}
100+
101+
React Compiler assumes that your code:
102+
103+
1. Is valid, semantic JavaScript
104+
2. Tests that nullable/optional values and properties are defined before accessing them (for example, by enabling [`strictNullChecks`](https://www.typescriptlang.org/tsconfig/#strictNullChecks) if using TypeScript), i.e., `if (object.nullableProperty) { object.nullableProperty.foo }` or with optional-chaining `object.nullableProperty?.foo`
105+
3. Follows the [Rules of React](https://react.dev/reference/rules)
83106

84-
## Installation {/*installation*/}
107+
React Compiler can verify many of the Rules of React statically, and will safely skip compilation when it detects an error. To see the errors we recommend also installing [eslint-plugin-react-compiler](https://www.npmjs.com/package/eslint-plugin-react-compiler).
108+
109+
### Should I try out the compiler? {/*should-i-try-out-the-compiler*/}
110+
111+
Please note that the compiler is still experimental and has many rough edges. While it has been used in production at companies like Meta, rolling out the compiler to production for your app will depend on the health of your codebase and how well you've followed the [Rules of React](/reference/rules).
112+
113+
**You don't have to rush into using the compiler now. It's okay to wait until it reaches a stable release before adopting it.** However, we do appreciate trying it out in small experiments in your apps so that you can [provide feedback](#reporting-issues) to us to help make the compiler better.
114+
115+
## Getting Started {/*getting-started*/}
116+
117+
In addition to these docs, we recommend checking the [React Compiler Working Group](https://github.com/reactwg/react-compiler) for additional information and discussion about the compiler.
85118

86119
### Checking compatibility {/*checking-compatibility*/}
87120

@@ -126,7 +159,48 @@ module.exports = {
126159
}
127160
```
128161

129-
### Usage with Babel {/*usage-with-babel*/}
162+
The eslint plugin will display any violations of the rules of React in your editor. When it does this, it means that the compiler has skipped over optimizing that component or hook. This is perfectly okay, and the compiler can recover and continue optimizing other components in your codebase.
163+
164+
**You don't have to fix all eslint violations straight away.** You can address them at your own pace to increase the amount of components and hooks being optimized, but it is not required to fix everything before you can use the compiler.
165+
166+
### Rolling out the compiler to your codebase {/*using-the-compiler-effectively*/}
167+
168+
#### Existing projects {/*existing-projects*/}
169+
The compiler is designed to compile functional components and hooks that follow the [Rules of React](/reference/rules). It can also handle code that breaks those rules by bailing out (skipping over) those components or hooks. However, due to the flexible nature of JavaScript, the compiler cannot catch every possible violation and may compile with false negatives: that is, the compiler may accidentally compile a component/hook that breaks the Rules of React which can lead to undefined behavior.
170+
171+
For this reason, to adopt the compiler successfully on existing projects, we recommend running it on a small directory in your product code first. You can do this by configuring the compiler to only run on a specific set of directories:
172+
173+
```js {3}
174+
const ReactCompilerConfig = {
175+
sources: (filename) => {
176+
return filename.indexOf('src/path/to/dir') !== -1;
177+
},
178+
};
179+
```
180+
181+
In rare cases, you can also configure the compiler to run in "opt-in" mode using the `compilationMode: "annotation"` option. This makes it so the compiler will only compile components and hooks annotated with a `"use memo"` directive. Please note that the `annotation` mode is a temporary one to aid early adopters, and that we don't intend for the `"use memo"` directive to be used for the long term.
182+
183+
```js {2,7}
184+
const ReactCompilerConfig = {
185+
compilationMode: "annotation",
186+
};
187+
188+
// src/app.jsx
189+
export default function App() {
190+
"use memo";
191+
// ...
192+
}
193+
```
194+
195+
When you have more confidence with rolling out the compiler, you can expand coverage to other directories as well and slowly roll it out to your whole app.
196+
197+
#### New projects {/*new-projects*/}
198+
199+
If you're starting a new project, you can enable the compiler on your entire codebase, which is the default behavior.
200+
201+
## Usage {/*installation*/}
202+
203+
### Babel {/*usage-with-babel*/}
130204

131205
<TerminalBlock>
132206
npm install babel-plugin-react-compiler
@@ -152,7 +226,7 @@ module.exports = function () {
152226

153227
`babel-plugin-react-compiler` should run first before other Babel plugins as the compiler requires the input source information for sound analysis.
154228

155-
### Usage with Vite {/*usage-with-vite*/}
229+
### Vite {/*usage-with-vite*/}
156230

157231
If you use Vite, you can add the plugin to vite-plugin-react:
158232

@@ -176,7 +250,7 @@ export default defineConfig(() => {
176250
});
177251
```
178252

179-
### Usage with Next.js {/*usage-with-nextjs*/}
253+
### Next.js {/*usage-with-nextjs*/}
180254

181255
Next.js has an experimental configuration to enable the React Compiler. It automatically ensures Babel is set up with `babel-plugin-react-compiler`.
182256

@@ -209,7 +283,7 @@ Using the experimental option ensures support for the React Compiler in:
209283
- Turbopack (opt-in through `--turbo`)
210284

211285

212-
### Usage with Remix {/*usage-with-remix*/}
286+
### Remix {/*usage-with-remix*/}
213287
Install `vite-plugin-babel`, and add the compiler's Babel plugin to it:
214288

215289
<TerminalBlock>
@@ -238,7 +312,7 @@ export default defineConfig({
238312
});
239313
```
240314

241-
### Usage with Webpack {/*usage-with-webpack*/}
315+
### Webpack {/*usage-with-webpack*/}
242316

243317
You can create your own loader for React Compiler, like so:
244318

@@ -275,37 +349,54 @@ function reactCompilerLoader(sourceCode, sourceMap) {
275349
module.exports = reactCompilerLoader;
276350
```
277351

278-
### Usage with Expo {/*usage-with-expo*/}
352+
### Expo {/*usage-with-expo*/}
279353

280354
Expo uses Babel via Metro, so refer to the [Usage with Babel](#usage-with-babel) section for installation instructions.
281355

282-
### Usage with React Native (Metro) {/*usage-with-react-native-metro*/}
356+
### Metro (React Native) {/*usage-with-react-native-metro*/}
283357

284358
React Native uses Babel via Metro, so refer to the [Usage with Babel](#usage-with-babel) section for installation instructions.
285359

286360
## Troubleshooting {/*troubleshooting*/}
287361

288-
### Reporting Issues {/*reporting-issues*/}
362+
To report issues, please first create a minimal repro on the [React Compiler Playground](https://playground.react.dev/) and include it in your bug report. You can open issues in the [facebook/react](https://github.com/facebook/react/issues) repo.
289363

290-
To report issues, please first create a minimal repro on the [React Compiler Playground](https://playground.react.dev/) and include it in your bug report.
364+
You can also provide feedback in the React Compiler Working Group by applying to be a member. Please see [the README for more details on joining](https://github.com/reactwg/react-compiler).
291365

292-
You can open issues in the [facebook/react](https://github.com/facebook/react/issues) repo.
366+
### `(0 , _c) is not a function` error {/*0--_c-is-not-a-function-error*/}
293367

294-
You can also provide feedback in the React Compiler Working Group by applying to be a member. Please see [the README for more details on joining](https://github.com/reactwg/react-compiler).
368+
This occurs if you are not using React 19 Beta and up. To fix this, [upgrade your app to React 19 Beta](https://react.dev/blog/2024/04/25/react-19-upgrade-guide) first.
369+
370+
If you are unable to upgrade to React 19, you may try a userspace implementation of the cache function as described in the [Working Group](https://github.com/reactwg/react-compiler/discussions/6). However, please note that this is not recommended and you should upgrade to React 19 when possible.
371+
372+
### How do I know my components have been optimized? {/*how-do-i-know-my-components-have-been-optimized*/}
373+
374+
[React Devtools](/learn/react-developer-tools) (v5.0+) has built-in support for React Compiler and will display a "Memo ✨" badge next to components that have been optimized by the compiler.
375+
376+
### Something is not working after compilation {/*something-is-not-working-after-compilation*/}
377+
If you have eslint-plugin-react-compiler installed, the compiler will display any violations of the rules of React in your editor. When it does this, it means that the compiler has skipped over optimizing that component or hook. This is perfectly okay, and the compiler can recover and continue optimizing other components in your codebase. **You don't have to fix all eslint violations straight away.** You can address them at your own pace to increase the amount of components and hooks being optimized.
295378

296-
### Common Issues {/*common-issues*/}
379+
Due to the flexible and dynamic nature of JavaScript however, it's not possible to comprehensively detect all cases. Bugs and undefined behavior such as infinite loops may occur in those cases.
297380

298-
#### `(0 , _c) is not a function` error {/*0--_c-is-not-a-function-error*/}
381+
If your app doesn't work properly after compilation and you aren't seeing any eslint errors, the compiler may be incorrectly compiling your code. To confirm this, try to make the issue go away by aggressively opting out any component or hook you think might be related via the [`"use no memo"` directive](#opt-out-of-the-compiler-for-a-component).
299382

300-
This occurs during JavaScript module evaluation when you are not using React 19 Beta and up. To fix this, [upgrade your app to React 19 Beta](https://react.dev/blog/2024/04/25/react-19-upgrade-guide) first.
383+
```js {2}
384+
function SuspiciousComponent() {
385+
"use no memo"; // opts out this component from being compiled by React Compiler
386+
// ...
387+
}
388+
```
301389

302-
### Debugging {/*debugging*/}
390+
<Note>
391+
#### `"use no memo"` {/*use-no-memo*/}
392+
393+
`"use no memo"` is a _temporary_ escape hatch that lets you opt-out components and hooks from being compiled by the React Compiler. This directive is not meant to be long lived the same way as eg [`"use client"`](/reference/rsc/use-client) is.
303394

304-
#### Checking if components have been optimized {/*checking-if-components-have-been-optimized*/}
305-
##### React DevTools {/*react-devtools*/}
395+
It is not recommended to reach for this directive unless it's strictly necessary. Once you opt-out a component or hook, it is opted-out forever until the directive is removed. This means that even if you fix the code, the compiler will still skip over compiling it unless you remove the directive.
396+
</Note>
306397

307-
React Devtools (v5.0+) has built-in support for React Compiler and will display a "Memo ✨" badge next to components that have been optimized by the compiler.
398+
When you make the error go away, confirm that removing the opt out directive makes the issue come back. Then share a bug report with us (you can try to reduce it to a small repro, or if it's open source code you can also just paste the entire source) using the [React Compiler Playground](https://playground.react.dev) so we can identify and help fix the issue.
308399

309-
##### Other issues {/*other-issues*/}
400+
### Other issues {/*other-issues*/}
310401

311402
Please see https://github.com/reactwg/react-compiler/discussions/7.

0 commit comments

Comments
 (0)