-
Notifications
You must be signed in to change notification settings - Fork 7.7k
[compiler] Rewrite React Compiler Docs #7868
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
--- | ||
title: Debugging and Troubleshooting | ||
--- | ||
|
||
<Intro> | ||
This guide helps you identify and fix issues when using React Compiler. Learn how to debug compilation problems and resolve common issues. | ||
</Intro> | ||
|
||
<YouWillLearn> | ||
|
||
* The difference between compiler errors and runtime issues | ||
* Common patterns that break compilation | ||
* Step-by-step debugging workflow | ||
|
||
</YouWillLearn> | ||
|
||
## Understanding Compiler Behavior {/*understanding-compiler-behavior*/} | ||
|
||
React Compiler is designed to handle code that follows the [Rules of React](/reference/rules). When it encounters code that might break these rules, it safely skips optimization rather than risk changing your app's behavior. | ||
|
||
### Compiler Errors vs Runtime Issues {/*compiler-errors-vs-runtime-issues*/} | ||
|
||
**Compiler errors** occur at build time and prevent your code from compiling. These are rare because the compiler is designed to skip problematic code rather than fail. | ||
|
||
**Runtime issues** occur when compiled code behaves differently than expected. Most of the time, if you encounter an issue with React Compiler, it's a runtime issue. This typically happens when your code violates the Rules of React in subtle ways that the compiler couldn't detect, and the compiler mistakenly compiled a component it should have skipped. | ||
|
||
When debugging runtime issues, focus your efforts on finding Rules of React violations in the affected components that were not detected by the ESLint rule. The compiler relies on your code following these rules, and when they're broken in ways it can't detect, that's when runtime problems occur. | ||
|
||
|
||
## Common Breaking Patterns {/*common-breaking-patterns*/} | ||
|
||
One of the main ways React Compiler can break your app is if your code was written to rely on memoization for correctness. This means your app depends on specific values being memoized to work properly. Since the compiler may memoize differently than your manual approach, this can lead to unexpected behavior like effects over-firing, infinite loops, or missing updates. | ||
|
||
Common scenarios where this occurs: | ||
|
||
- **Effects that rely on referential equality** - When effects depend on objects or arrays maintaining the same reference across renders | ||
- **Dependency arrays that need stable references** - When unstable dependencies cause effects to fire too often or create infinite loops | ||
- **Conditional logic based on reference checks** - When code uses referential equality checks for caching or optimization | ||
|
||
## Debugging Workflow {/*debugging-workflow*/} | ||
|
||
Follow these steps when you encounter issues: | ||
|
||
### Compiler Build Errors {/*compiler-build-errors*/} | ||
|
||
If you encounter a compiler error that unexpectedly breaks your build, this is likely a bug in the compiler. Report it to the [facebook/react](https://github.com/facebook/react/issues) repository with: | ||
- The error message | ||
- The code that caused the error | ||
- Your React and compiler versions | ||
|
||
### Runtime Issues {/*runtime-issues*/} | ||
|
||
For runtime behavior issues: | ||
|
||
### 1. Temporarily Disable Compilation {/*temporarily-disable-compilation*/} | ||
|
||
Use `"use no memo"` to isolate whether an issue is compiler-related: | ||
|
||
```js | ||
function ProblematicComponent() { | ||
"use no memo"; // Skip compilation for this component | ||
// ... rest of component | ||
} | ||
``` | ||
|
||
If the issue disappears, it's likely related to a Rules of React violation. | ||
|
||
You can also try removing manual memoization (useMemo, useCallback, memo) from the problematic component to verify that your app works correctly without any memoization. If the bug still occurs when all memoization is removed, you have a Rules of React violation that needs to be fixed. | ||
|
||
### 2. Fix Issues Step by Step {/*fix-issues-step-by-step*/} | ||
|
||
1. Identify the root cause (often memoization-for-correctness) | ||
2. Test after each fix | ||
3. Remove `"use no memo"` once fixed | ||
4. Verify the component shows the ✨ badge in React DevTools | ||
|
||
## Reporting Compiler Bugs {/*reporting-compiler-bugs*/} | ||
|
||
If you believe you've found a compiler bug: | ||
|
||
1. **Verify it's not a Rules of React violation** - Check with ESLint | ||
2. **Create a minimal reproduction** - Isolate the issue in a small example | ||
3. **Test without the compiler** - Confirm the issue only occurs with compilation | ||
4. **File an [issue](https://github.com/facebook/react/issues/new?template=compiler_bug_report.yml)**: | ||
- React and compiler versions | ||
- Minimal reproduction code | ||
- Expected vs actual behavior | ||
- Any error messages | ||
|
||
## Next Steps {/*next-steps*/} | ||
|
||
- Review the [Rules of React](/reference/rules) to prevent issues | ||
- Check the [incremental adoption guide](/learn/react-compiler/incremental-adoption) for gradual rollout strategies |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,225 @@ | ||||||
--- | ||||||
title: Incremental Adoption | ||||||
--- | ||||||
|
||||||
<Intro> | ||||||
React Compiler can be adopted incrementally, allowing you to try it on specific parts of your codebase first. This guide shows you how to gradually roll out the compiler in existing projects. | ||||||
</Intro> | ||||||
|
||||||
<YouWillLearn> | ||||||
|
||||||
* Why incremental adoption is recommended | ||||||
* Using Babel overrides for directory-based adoption | ||||||
* Using the "use memo" directive for opt-in compilation | ||||||
* Using the "use no memo" directive to exclude components | ||||||
* Runtime feature flags with gating | ||||||
* Monitoring your adoption progress | ||||||
|
||||||
</YouWillLearn> | ||||||
|
||||||
## Why Incremental Adoption? {/*why-incremental-adoption*/} | ||||||
|
||||||
While React Compiler is designed to handle most React code automatically, adopting it incrementally allows you to: | ||||||
|
||||||
- Test the compiler on a small portion of your app first | ||||||
- Identify and fix any Rules of React violations | ||||||
- Build confidence before expanding to your entire codebase | ||||||
- Minimize risk in production applications | ||||||
|
||||||
## Approaches to Incremental Adoption {/*approaches-to-incremental-adoption*/} | ||||||
|
||||||
There are three main approaches to adopt React Compiler incrementally: | ||||||
|
||||||
1. **Babel overrides** - Apply the compiler to specific directories | ||||||
2. **Opt-in with "use memo"** - Only compile components that explicitly opt in | ||||||
3. **Runtime gating** - Control compilation with feature flags | ||||||
|
||||||
All approaches allow you to test the compiler on specific parts of your application before full rollout. | ||||||
|
||||||
## Directory-Based Adoption with Babel Overrides {/*directory-based-adoption*/} | ||||||
|
||||||
Babel's `overrides` option lets you apply different plugins to different parts of your codebase. This is ideal for gradually adopting React Compiler directory by directory. | ||||||
|
||||||
### Basic Configuration {/*basic-configuration*/} | ||||||
|
||||||
Start by applying the compiler to a specific directory: | ||||||
|
||||||
```js | ||||||
// babel.config.js | ||||||
module.exports = { | ||||||
plugins: [ | ||||||
// Global plugins that apply to all files | ||||||
], | ||||||
overrides: [ | ||||||
{ | ||||||
test: './src/modern/**/*.{js,jsx,ts,tsx}', | ||||||
plugins: [ | ||||||
'babel-plugin-react-compiler' | ||||||
] | ||||||
} | ||||||
] | ||||||
}; | ||||||
``` | ||||||
|
||||||
### Expanding Coverage {/*expanding-coverage*/} | ||||||
|
||||||
As you gain confidence, add more directories: | ||||||
|
||||||
```js | ||||||
// babel.config.js | ||||||
module.exports = { | ||||||
plugins: [ | ||||||
// Global plugins | ||||||
], | ||||||
overrides: [ | ||||||
{ | ||||||
test: ['./src/modern/**/*.{js,jsx,ts,tsx}', './src/features/**/*.{js,jsx,ts,tsx}'], | ||||||
plugins: [ | ||||||
'babel-plugin-react-compiler' | ||||||
] | ||||||
}, | ||||||
{ | ||||||
test: './src/legacy/**/*.{js,jsx,ts,tsx}', | ||||||
plugins: [ | ||||||
// Different plugins for legacy code | ||||||
] | ||||||
} | ||||||
] | ||||||
}; | ||||||
``` | ||||||
|
||||||
### With Compiler Options {/*with-compiler-options*/} | ||||||
|
||||||
You can also configure compiler options per override: | ||||||
|
||||||
```js | ||||||
// babel.config.js | ||||||
module.exports = { | ||||||
plugins: [], | ||||||
overrides: [ | ||||||
{ | ||||||
test: './src/experimental/**/*.{js,jsx,ts,tsx}', | ||||||
plugins: [ | ||||||
['babel-plugin-react-compiler', { | ||||||
compilationMode: 'annotation', // Only compile "use memo" components | ||||||
panicThreshold: 'NONE' // More permissive for experimental code | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
lowercased to match the types and be more consistent with |
||||||
}] | ||||||
] | ||||||
}, | ||||||
{ | ||||||
test: './src/production/**/*.{js,jsx,ts,tsx}', | ||||||
plugins: [ | ||||||
['babel-plugin-react-compiler', { | ||||||
panicThreshold: 'CRITICAL_ERRORS' // Stricter for production code | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
}] | ||||||
] | ||||||
} | ||||||
] | ||||||
}; | ||||||
``` | ||||||
|
||||||
|
||||||
## Opt-in Mode with "use memo" {/*opt-in-mode-with-use-memo*/} | ||||||
|
||||||
For maximum control, you can use `compilationMode: 'annotation'` to only compile components and hooks that explicitly opt in with the `"use memo"` directive. | ||||||
|
||||||
<Note> | ||||||
This approach gives you fine-grained control over individual components and hooks. It's useful when you want to test the compiler on specific components without affecting entire directories. | ||||||
</Note> | ||||||
|
||||||
### Annotation Mode Configuration {/*annotation-mode-configuration*/} | ||||||
|
||||||
```js | ||||||
// babel.config.js | ||||||
module.exports = { | ||||||
plugins: [ | ||||||
['babel-plugin-react-compiler', { | ||||||
compilationMode: 'annotation', | ||||||
}], | ||||||
], | ||||||
}; | ||||||
``` | ||||||
|
||||||
### Using the Directive {/*using-the-directive*/} | ||||||
|
||||||
Add `"use memo"` at the beginning of functions you want to compile: | ||||||
|
||||||
```js | ||||||
function TodoList({ todos }) { | ||||||
"use memo"; // Opt this component into compilation | ||||||
|
||||||
const sortedTodos = todos.slice().sort(); | ||||||
|
||||||
return ( | ||||||
<ul> | ||||||
{sortedTodos.map(todo => ( | ||||||
<TodoItem key={todo.id} todo={todo} /> | ||||||
))} | ||||||
</ul> | ||||||
); | ||||||
} | ||||||
|
||||||
function useSortedData(data) { | ||||||
"use memo"; // Opt this hook into compilation | ||||||
|
||||||
return data.slice().sort(); | ||||||
} | ||||||
``` | ||||||
|
||||||
With `compilationMode: 'annotation'`, you must: | ||||||
- Add `"use memo"` to every component you want optimized | ||||||
- Add `"use memo"` to every custom hook | ||||||
- Remember to add it to new components | ||||||
|
||||||
This gives you precise control over which components are compiled while you evaluate the compiler's impact. | ||||||
|
||||||
## Runtime Feature Flags with Gating {/*runtime-feature-flags-with-gating*/} | ||||||
|
||||||
The `gating` option enables you to control compilation at runtime using feature flags. This is useful for running A/B tests or gradually rolling out the compiler based on user segments. | ||||||
|
||||||
### How Gating Works {/*how-gating-works*/} | ||||||
|
||||||
The compiler wraps optimized code in a runtime check. If the gate returns `true`, the optimized version runs. Otherwise, the original code runs. | ||||||
|
||||||
### Gating Configuration {/*gating-configuration*/} | ||||||
|
||||||
```js | ||||||
// babel.config.js | ||||||
module.exports = { | ||||||
plugins: [ | ||||||
['babel-plugin-react-compiler', { | ||||||
gating: { | ||||||
source: 'ReactCompilerFeatureFlags', | ||||||
importSpecifierName: 'isCompilerEnabled', | ||||||
}, | ||||||
}], | ||||||
], | ||||||
}; | ||||||
``` | ||||||
|
||||||
### Implementing the Feature Flag {/*implementing-the-feature-flag*/} | ||||||
|
||||||
Create a module that exports your gating function: | ||||||
|
||||||
```js | ||||||
// ReactCompilerFeatureFlags.js | ||||||
export function isCompilerEnabled() { | ||||||
// Use your feature flag system | ||||||
return getFeatureFlag('react-compiler-enabled'); | ||||||
} | ||||||
``` | ||||||
|
||||||
## Troubleshooting Adoption {/*troubleshooting-adoption*/} | ||||||
|
||||||
If you encounter issues during adoption: | ||||||
|
||||||
1. Use `"use no memo"` to temporarily exclude problematic components | ||||||
2. Check the [debugging guide](/learn/react-compiler/debugging) for common issues | ||||||
3. Fix Rules of React violations identified by the ESLint plugin | ||||||
4. Consider using `compilationMode: 'annotation'` for more gradual adoption | ||||||
|
||||||
## Next Steps {/*next-steps*/} | ||||||
|
||||||
- Read the [configuration guide](/reference/react-compiler/configuration) for more options | ||||||
- Learn about [debugging techniques](/learn/react-compiler/debugging) | ||||||
- Check the [API reference](/reference/react-compiler/configuration) for all compiler options |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
--- | ||
title: React Compiler | ||
--- | ||
|
||
## Introduction {/*introduction*/} | ||
|
||
Learn [what React Compiler does](/learn/react-compiler/introduction) and how it automatically optimizes your React application by handling memoization for you, eliminating the need for manual `useMemo`, `useCallback`, and `React.memo`. | ||
|
||
## Installation {/*installation*/} | ||
|
||
Get started with [installing React Compiler](/learn/react-compiler/installation) and learn how to configure it with your build tools. | ||
|
||
|
||
## Incremental Adoption {/*incremental-adoption*/} | ||
|
||
Learn [strategies for gradually adopting React Compiler](/learn/react-compiler/incremental-adoption) in your existing codebase if you're not ready to enable it everywhere yet. | ||
|
||
## Debugging and Troubleshooting {/*debugging-and-troubleshooting*/} | ||
|
||
When things don't work as expected, use our [debugging guide](/learn/react-compiler/debugging) to understand the difference between compiler errors and runtime issues, identify common breaking patterns, and follow a systematic debugging workflow. | ||
|
||
## Configuration and Reference {/*configuration-and-reference*/} | ||
|
||
For detailed configuration options and API reference: | ||
|
||
- [Configuration Options](/reference/react-compiler/configuration) - All compiler configuration options including React version compatibility | ||
- [Directives](/reference/react-compiler/directives) - Function-level compilation control | ||
- [Compiling Libraries](/reference/react-compiler/compiling-libraries) - Shipping pre-compiled libraries | ||
|
||
## Additional resources {/*additional-resources*/} | ||
|
||
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. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another breaking pattern is when you reference a variable declaration inside a callback that was declared after the callback. This can happen accidentally when writing an especially complex component or hook. That callback won't be memoized by the compiler.
Would you consider this a common scenario?
https://playground.react.dev/#N4Igzg9grgTgxgUxALhAMygOzgFwJYSYAEAQgIYAmAFAJRHAA6xRchYORhAwgDZ5wBrIgF4itEQD56TIrJZsIPBADoeEAOZU0ECDRlEAvvv2tM7IgG1tEADREwCHADEdAXRFEoDgMo4yOBCoABj1MfRhHWGIAHgo8ADdOTF5+AWFgbj5BAwlgawNogHo4+IkmIzDMDGx8QiIAcR1qOkZmU3MrHTsHZzcPLwRff0CQkzYOTNSPcWEpVrl5M0UVNU1rUNkK8MiYGJKklMF0yezc-KKSsswDEAMgA
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this is a fun case. Note that you can't memoize such callbacks yourself — adding a
useCallback()
in the "Bad" example in your playground link would be a TDZ violation when you added the dep onfoo
:We can look into adding linting against this pattern to suggest moving the callback. Note that the compiler will already reject this code if you try to manually memoize it, because the dependency changes later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this pattern doesn't work with manual memoization, this pattern is most likely in code that isn't memoized already. Definitely worth adding validation to help identify opportunities in such code to memoize more.