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.
React Compiler is designed to optimize your entire codebase automatically, but you don’t have to adopt it all at once. Incremental adoption gives you control over the rollout process, letting you test the compiler on small parts of your app before expanding to the rest.
Starting small helps you build confidence in the compiler’s optimizations. You can verify that your app behaves correctly with compiled code, measure performance improvements, and identify any edge cases specific to your codebase. This approach is especially valuable for production applications where stability is critical.
Incremental adoption also makes it easier to address any Rules of React violations the compiler might find. Instead of fixing violations across your entire codebase at once, you can tackle them systematically as you expand compiler coverage. This keeps the migration manageable and reduces the risk of introducing bugs.
By controlling which parts of your code get compiled, you can also run A/B tests to measure the real-world impact of the compiler’s optimizations. This data helps you make informed decisions about full adoption and demonstrates the value to your team.
There are three main approaches to adopt React Compiler incrementally:
All approaches allow you to test the compiler on specific parts of your application before full rollout.
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.
Start by applying the compiler to a specific directory:
// 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'
]
}
]
}; As you gain confidence, add more directories:
// 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
]
}
]
}; You can also configure compiler options per override:
// babel.config.js
module.exports = {
plugins: [],
overrides: [
{
test: './src/experimental/**/*.{js,jsx,ts,tsx}',
plugins: [
['babel-plugin-react-compiler', {
// options ...
}]
]
},
{
test: './src/production/**/*.{js,jsx,ts,tsx}',
plugins: [
['babel-plugin-react-compiler', {
// options ...
}]
]
}
]
}; For maximum control, you can use compilationMode: 'annotation' to only compile components and hooks that explicitly opt in with the "use memo" directive.
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.
// babel.config.js
module.exports = {
plugins: [
['babel-plugin-react-compiler', {
compilationMode: 'annotation',
}],
],
}; Add "use memo" at the beginning of functions you want to compile:
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:
"use memo" to every component you want optimized"use memo" to every custom hookThis gives you precise control over which components are compiled while you evaluate the compiler’s impact.
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.
The compiler wraps optimized code in a runtime check. If the gate returns true, the optimized version runs. Otherwise, the original code runs.
// babel.config.js
module.exports = {
plugins: [
['babel-plugin-react-compiler', {
gating: {
source: 'ReactCompilerFeatureFlags',
importSpecifierName: 'isCompilerEnabled',
},
}],
],
}; Create a module that exports your gating function:
// ReactCompilerFeatureFlags.js
export function isCompilerEnabled() {
// Use your feature flag system
return getFeatureFlag('react-compiler-enabled');
} If you encounter issues during adoption:
"use no memo" to temporarily exclude problematic componentscompilationMode: 'annotation' for more gradual adoption
© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://react.dev/learn/react-compiler/incremental-adoption