React Compiler Discovery
Helpful References
What is React Compiler?
From React Compiler - React (What does the compiler do?) :
In order to optimize applications, React Compiler automatically memoizes your code. You may be familiar today with memoization through APIs such as
useMemo
,useCallback
, andReact.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.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.
Why is React Compiler something we want to use?
The main benefits I see from React Compiler regard maintainability. Memoization can be tricky to get right, and being able to rely on the compiler for that aspect of performance will allow us to have all the performance benefits without needing to maintain handwritten memoization code.
Things I’ve Learned
React Compiler is currently in Beta (as of writing 2025/01/30)
React Compiler can be used with React 17, 18, or 19 React Compiler - React (Using React Compiler with React 17 or 18)
There is a community Webpack loader for React Compiler
React Compiler is fairly strict about projects following the Rules of React
There is a React Compiler eslint plugin that can be used even if React Compiler itself isn’t being used
What I’ve Tried
In order to test out the possibility of using React Compiler on our MFEs, I decided to try setting it up for frontend-app-learning
. The first thing I did was install dependencies
diff --git a/package.json b/package.json
index 80d11b8..d37bffd 100644
--- a/package.json
+++ b/package.json
@@ -64,6 +64,7 @@
"prop-types": "15.8.1",
"query-string": "^7.1.3",
"react": "17.0.2",
+ "react-compiler-runtime": "^19.0.0-beta-27714ef-20250124",
"react-dom": "17.0.2",
"react-helmet": "6.1.0",
"react-redux": "7.2.9",
@@ -87,9 +88,11 @@
"axios-mock-adapter": "2.1.0",
"bundlewatch": "^0.4.0",
"eslint-import-resolver-webpack": "^0.13.9",
+ "eslint-plugin-react-compiler": "^19.0.0-beta-27714ef-20250124",
"jest": "^29.7.0",
"jest-console-group-reporter": "^1.1.1",
"jest-when": "^3.6.0",
+ "react-compiler-webpack": "^0.1.2",
"rosie": "2.1.1"
},
"bundlewatch": {
I then updated the webpack dev config to use React Compiler in local testing
diff --git a/webpack.dev.config.js b/webpack.dev.config.js
index ddf63de..2a0a9c6 100644
--- a/webpack.dev.config.js
+++ b/webpack.dev.config.js
@@ -1,5 +1,7 @@
const path = require('path');
const { createConfig } = require('@openedx/frontend-build');
+// You can leverage your IDE's Intellisense (autocompletion, type check, etc.) with the helper function `defineReactCompilerLoaderOption`:
+const { defineReactCompilerLoaderOption, reactCompilerLoader } = require('react-compiler-webpack');
const config = createConfig('webpack-dev');
@@ -8,4 +10,24 @@ config.resolve.alias = {
'@src': path.resolve(__dirname, 'src'),
};
+config.module.rules.push(
+ {
+ test: /\.[mc]?[jt]sx?$/i,
+ exclude: /node_modules/,
+ use: [
+ // babel-loader, swc-loader, esbuild-loader, or anything you like to transpile JSX should go here.
+ // If you are using rspack, the rspack's buiilt-in react transformation is sufficient.
+ // { loader: 'swc-loader' },
+ // Now add forgetti-loader
+ {
+ loader: reactCompilerLoader,
+ options: defineReactCompilerLoaderOption({
+ // React Compiler options goes here
+ target: '17'
+ })
+ }
+ ]
+ }
+);
+
module.exports = config;
After that, I tried starting the MFE with npm run dev
and loading the demo course. I immediately hit the following error:
I did a bit of searching and found a bug report from someone who encountered the same error when trying to use React Compiler with their project
My next thought was that this issue may be something the React Compiler eslint plugin would catch, so I decided to set that up
diff --git a/.eslintrc.js b/.eslintrc.js
index 97be119..5309f19 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -2,6 +2,9 @@
const { createConfig } = require('@openedx/frontend-build');
const config = createConfig('eslint', {
+ plugins: [
+ 'eslint-plugin-react-compiler',
+ ],
rules: {
// TODO: all these rules should be renabled/addressed. temporarily turned off to unblock a release.
'react-hooks/rules-of-hooks': 'off',
@@ -11,6 +14,7 @@ const config = createConfig('eslint', {
'react/jsx-no-useless-fragment': 'off',
'react/no-unknown-property': 'off',
'func-names': 'off',
+ 'react-compiler/react-compiler': 'error',
},
settings: {
'import/resolver': {
After which running npm run lint
led to the following errors:
20 “React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled.” errors
2 “Hooks must always be called in a consistent order” errors
2 “Hooks must be called at the top level in the body of a function component or custom hook” errors
1 “Writing to a variable defined outside a component or hook is not allowed” error
1 “Unexpected reassignment of a variable which was defined outside of the component.” error
Most of the non “skipped optimizing” errors come from not following Rules of Hooks – React
Proposed Next Steps
For frontend-app-learning
specifically, I think simply removing ‘react-hooks/rules-of-hooks': ‘off',
from .eslintrc.js
is a great first step.
Looking beyond the one MFE, I think a great next step to move us closer to being able to use React Compiler when it moves out of beta is to start using the react-compiler/react-compiler
eslint rules. If we add the eslint rules to frontend-build
as warnings we’ll be able to work through those without breaking anything, and eventually turn them into errors when we’re ready to push towards actually using React Compiler.