-
Notifications
You must be signed in to change notification settings - Fork 429
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
feat: add support for React Compiler beta #7702
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
1 Skipped Deployment
|
New and removed dependencies detected. Learn more about Socket for GitHub ↗︎
🚮 Removed packages: npm/[email protected], npm/[email protected], npm/[email protected] |
No changes to documentation |
⚡️ Editor Performance ReportUpdated Tue, 19 Nov 2024 16:47:51 GMT
Detailed information🏠 Reference resultThe performance result of
🧪 Experiment resultThe performance result of this branch
📚 Glossary
|
Component Testing Report Updated Nov 19, 2024 4:45 PM (UTC) ✅ All Tests Passed -- expand for details
|
58a439a
to
d40e208
Compare
d40e208
to
e76deb8
Compare
e76deb8
to
dbaff3b
Compare
dbaff3b
to
f1b90e9
Compare
f1b90e9
to
2489d3a
Compare
This makes sense :) thank you for answering it 🙏 |
e232ca1
to
60000bc
Compare
60000bc
to
f7600a4
Compare
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.
I agree with your suggestions for CLI improvements - I will see if we can make at least some of those changes available in follow-up PR(s) shortly. For now, documenting ("publicly") how to enable the React compiler and which dependencies to install would be a good first step.
Sure @rexxars, I'll get right on that, similar to the react 19 docs page 🙌 |
Co-authored-by: Espen Hovlandsdal <[email protected]>
Co-authored-by: Espen Hovlandsdal <[email protected]>
* next: (37 commits) v3.65.0 fix(deps): update dependency @sanity/insert-menu to v1.0.13 (#7880) fix(deps): update dependency @sanity/presentation to v1.18.5 (#7881) fix(deps): Update dev-non-major (#7875) fix(deps): update dependency @sanity/ui to ^2.8.25 (#7876) fix(telemetry): log (well known) workspace and dataset names (#7859) fix(deps): update dependency @portabletext/editor to ^1.11.3 (#7865) fix(deps): update dependency react-rx to ^4.1.7 (#7874) chore(deps): dedupe pnpm-lock.yaml (#7873) chore(ci): max react compiler warnings are now 79 fix(deps): update React Compiler dependencies 🤖 ✨ (#7872) fix(core): discard announcements dialog onClickOutside (#7863) feat(structure): History UI updates (#7462) chore(cli): temporarily disable telemetry tests (#7845) chore(deps): update dependency turbo to ^2.3.0 (#7846) chore(lockfile): update dependency @sanity/asset-utils to v2.2.0 (#7847) chore(lockfile): update dependency react-focus-lock to v2.13.2 (#7848) feat: add support for React Compiler beta (#7702) v3.64.3 fix(deps): update dependency @portabletext/editor to ^1.11.1 (#7861) ...
Description
The React Compiler is now in Beta:
This PR adds the React Compiler to the Studio in two ways:
1. Pre-compiling
sanity
and@sanity/vision
We've been testing it for some time now in these libraries:
react-rx
@sanity/ui
@portabletext/editor
And it has worked really well for us.
react-rx
is the only library that is fully compiled at the moment, while:@portabletext/editor
is nearly there (9 out of 13 components compiled, 11 linter warnings left to fix)@sanity/ui
also close, 290 out of 305 components, 10 linter warnings left to fixWhy is `@sanity/ui` reporting a higher number of bailouts (15 components) than the linter?
To see the report of components that can be compiled, run
npx react-compiler-healthcheck
. Theeslint-plugin-react-compiler
is intended to only report issues that are actionable, things you should fix that allows the component to be compiled. Thus it's normal to see discrepancies like in@sanity/ui
. You can opt-in the linter to report all bailouts, including those that are due to patterns that will be supported in the future and are considered to be valid:pnpm eslint --cache --no-inline-config --no-eslintrc --ignore-pattern '**/__tests__/**' --ext .cjs,.mjs,.js,.jsx,.ts,.tsx --parser @typescript-eslint/parser --plugin react-compiler --plugin react-hooks --rule 'react-compiler/react-compiler: [error,{__unstable_donotuse_reportAllBailouts: true}]' src
.The benefit of pre-compiling is that "it just works", people userland bump their deps and the Studio is now faster ✨ It supports React 18 and upwards.
Plugin authors that use `@sanity/pkg-utils` can ship pre-compiled plugins as well
npm install --save-exact --save-dev babel-plugin-react-compiler@beta && npm install --save-exact react-compiler-runtime@beta
(it's important that exact versions are used while in beta, and thatreact-compiler-runtime
is a direct dependency, not indevDependencies
).package.config.ts
:It makes sense to add it to
@sanity/plugin-kit
once the React Compiler itself is GA and no longer beta.@sanity/vision
Running
cd packages/@sanity/vision/src && pnpx react-compiler-healthcheck
yields:That doesn't mean everything is compiled though, as the main component
<VisionGui />
is a class component and thus is unsupported by the React Compiler. The difficulty of auto memoizing class components was one of the motivators behind the introduction of React Hooks and the shift away from classes, and so<VisionGui />
will have to be refactored to a function component for it to be optimised fully.sanity
Running
cd packages/sanity/src && pnpx react-compiler-healthcheck
yields:86% coverage is awesome, at the same time it's important to consider that:
<CommandList />
<GetFormValueProvider />
<RouteScope />
<DocumentPaneProvider />
<DocumentPanel />
<FormView />
<ChangesInspector />
<DocumentListPaneContent />
<ArrayOfPrimitivesInput />
<BaseFileInput />
<ToolCanvasLegacy />
Until these are handled we won't see the full potential of the React Compiler, as it's not invulnerable to unstable props:
In this scenario the performance improvement on
OptimizedComponent
will appear minimal, until one dayUnoptimizedComponent
is fixed and now you'll have a dramatic delta as the React Compiler is able to deliver fine grained memoization and reduced rerenders to only what's absolutely necessary, end to end.Also worth noting is that there are some known issues with
@tanstack/react-table
and the React Compiler, so I've added'use no memo'
on the affected files to disable the compiler there. The way references are used in that library is highly unsafe, and is causing React to no longer be fully reactive:And the effect it has is that when React Compiler memoizes our code,
@tanstack/react-table
stops re-rendering when it should because it's relying on unstable dependencies to reach eventual consistency and with the React Compiler enabled they're no longer unstable.If we run into similar issues the best resolution is to add
'use no memo'
at the top of that file so that we don't need to disable the React Compiler everywhere if a regression is discovered.The strategy moving forward is to gradually refactor components so they can be compiled, and to work with the React Core Team and report issues or missing functionality so that the React Compiler can learn to understand patterns we use that follow the Rules of React and are safe, but not yet supported.
With this strategy we should see the Studio codebase get gradually faster, and when we hit critical coverage it'll suddenly speed up significantly when unstable props and context values no longer poke holes through the
1218
components that are memoized from the start.2. Allow userland to opt-in to React Compiler
As mentioned in the previous section, even if something is fully memoized by the React Compiler, its performance improvements can be negated if parent components are sending props or context provider values that are unstable.
Thus custom studio components and code can have a major impact on the overall studio performance. We've seen customers face severe degradation simply because a single function given as a prop forgot to use the
React.useCallback
wrapper.Such scenarios can be prevented if the React Compiler is enabled on custom studio code.
Additionally, the React Compiler Working Group is now open for everyone. Sanity users with complex studios is a treasure trove of code that can test the correctness of React Compiler, and we'll benefit from giving them the opportunity to participate in the working group, and give their feedback.
For users on React 18, opt-in involves installing
react-compiler-runtime@beta
as a direct dependency, andbabel-plugin-react-compiler@beta
as a dev dependency, and then add this to theirsanity.cli.ts
to enable the compiler:For React 19 you only need
babel-plugin-react-compiler@beta
in your dev dependencies, and:The official docs can then be used to decide how to adopt it, test it and more.
Until the React Compiler is stable, it's best to leave it as opt-in, and to require installing the
babel-plugin-react-compiler
andreact-compiler-runtime
dependencies in userland. It enables userland to test bugfixes that are only available onbabel-plugin-react-compiler@experimental
, and decouples the version we use to pre-compilesanity
from userland.Decoupling them is important, to ensure that userland with React Compiler can update deps like
sanity
anytime they like, without risking changes to how the React Compiler itself works and optimises their code. They are in control over that with when they updatebabel-plugin-react-compiler
. It's also useful to havebabel-plugin-react-compiler
andeslint-plugin-react-compiler
on the same exact version, so that they behave the same.What to review
I've published a prerelease as
pnpm add sanity@react-compiler
that can be used to more accurately experience how it's like in userland.On the Test Studio on the monorepo, due to how we enable HMR and such, it's easier to always enable the React Compiler. On our monorepo, enabling React Compiler enables it for
sanity
,@sanity/vision
as well as custom studio code. There's no easy way on the monorepo to test scenarios where onlysanity
and@sanity/vision
is compiled, and not userland. This is due to@sanity/pkg-utils
only being used during publishing, and not on the monorepo.Other differences are that the monorepo installs
babel-plugin-react-compiler
andreact-compiler-runtime
, so it's difficult to test what happens if userland wants to use the compiler but forgot to install these deps.pnpm add sanity@react-compiler
allows you to experience that.And with that said, there where a couple of things I'd want the CLI to do but I couldn't figure out:
We should detect if the wrong
reactCompiler.target
is usedIf
react
is on version 18, then setting this should fail onsanity dev
orsanity build
:as
target
must be18
.Likewise, if
package.json
says:"react": "19.0.0-rc-f994737d14-20240522"
thentarget
should be19
.We could warn here instead of erroring, as it'll still work, it's just not ideal as the native React Compiler runtime that ships in React 19 performs better than the
react-compiler-runtime
polyfill.We should validate that
react-compiler-runtime
is installedIf we're on
react
18 we should throw onsanity dev
andsanity build
ifreact-compiler-runtime
isn't installed as a direct dependency:These should pass if it's in the
package.json
that hasreact
,react-dom
,sanity
andstyled-components
installed:If the semver includes
^
,*
or other in-exact ranges then it should fail as it's not safe. Only exact version numbers, or a dist-tag name, is safe.Likewise, it shouldn't be a
devDependencies
norpeerDependencies
.We should validate that
babel-plugin-react-compiler
is installedSince we intentionally don't ship
babel-plugin-react-compiler
as a direct dependency ofsanity
, it has to be installed in userland.We should require it to be in
devDependencies
, and to follow the same semver rules asreact-compiler-runtime
.Other tooling like
next
validates for this, as the error coming fromvite
if we don't is difficult to wrap your head around.Ensure that if
eslint-plugin-react-compiler
is installed it is on the same version asbabel-plugin-react-compiler
Both plugins ship with the same underlying compiler, and are published with the same version numbers and dist-tags. It's important that they match so that you don't end up with a linter that has warnings that the babel plugin now supports and vice versa.
Consider
autoUpdates
supportI didn't update
@repo/package.bundle
with the compiler, so studios that have--auto-updates
in theirsanity build
, orautoUpdates: true
in theirsanity.cli.ts
, will not see the compiler used onsanity
or@sanity/vision
. They will see it on their custom code, and it'll work well for react 19 users, but for react 18 there's going to be some bloat asreact-compiler-runtime
is bundled into the custom studio codebase, instead of using a shared dep like the way we handlereact
,react-dom
andstyled-components
.Consider easier setup, like Next.js
Next.js lets you enable the compiler with just a boolean, no need to set a
target
. It would b super sweet if all you needed to do was settingreactCompiler: true
, and we'd then:target
should be by checking thereact
major that's installed.babel-plugin-react-compiler@beta
automatically to dev deps if needed.react-compiler-runtime@beta
as a direct dep if needed.I don't know how to add those things to our CLI but if you do feel free to push commits to this PR and add it ❤
How to test and verify that it works
Components that are memoized gets a new badge in the React Devtools:

You can inspect the regular test studio deployment on this PR, or run
pnpm dev
, as well aspnpm dev:next-studio
to see it on React 19. It's enabled on all of them (after this merges I'll remove thepnpm dev:compiled-studio
and its vercel deployment as it's no longer needed).Worst case these components should work like before, best case they'll work faster.
Additionally, the eFPS testing suite is updated to use the compiler, so its results are a good indicator of how much perf has improved.
Maintaining React Compiler deps
Renovatebot is struggling to understand how to deal with the pre-release version hashes used for deps like
pnpm add react-compiler-runtime@beta
.The
.github/worfklows/react-compiler.yml
workflow added in this PR is already in use on other repos, the PRs it creates looks like this: sanity-io/ui#1468It's setup to run after the weekly monday beta release [the React Core team runs](https://github.com/facebook/react/blob/989af12f72080c17db03ead91d99b6394a215564/.github/workflows/compiler_prereleases_weekly.yml#L5-L6
workflow_dispatch:).
Testing
All the testing suites, vitest, playwright, eFPS, everything is setup to use the compiler, to ensure we test it as much as possible.
I tried to setup a dual test runner setup like we do on
react-rx
, as it can be helpful to validate "correctness" (does the component still work the same if the compiler is turned off again), but I couldn't get it to work (maybe @bjoerge knows how?).It might be useful to run E2E tests the same way, but when it all comes down to it it's not critical.
If something doesn't work, adding
'use no memo'
at the top of that file opts it out on all these layers, allowing us to quickly test correctness whenever we need, and issue hotfixes that solves the worst case scenario of a regression, without needing to turn the compiler entirely off.Notes for release
The
sanity
and@sanity/vision
are now precompiled with the React Compiler.With 1 231 out of 1 411 being compiled, we're already seeing a 20%-30% overall reduction in render time and latency. We expect that to improve significantly as we gradually refactor the remaining 180 components so they can be auto memoized.
For a few weeks now we've already been shipping our Design System
@sanity/ui
with React Compiler, as well as@portabletext/editor
which is used by PortableText input fields, andreact-rx
which runs vital state management throughout the Studio codebase.We continue to see impressive results in our profiling and analysis, and with the release of React Compiler in beta we are excited to add support for using it to auto memoize your own studio code:
pnpm add react-compiler-runtime@beta && pnpm add --save-dev babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta
yarn add react-compiler-runtime@beta && yarn add --save-dev babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta
npm install --save-exact react-compiler-runtime@beta && npm install --save-exact --save-dev babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta
And in your
sanity.cli.ts
add:import {defineCliConfig} from 'sanity/cli' export default defineCliConfig({ api: { projectId: 'abc123', dataset: 'production', }, reactStrictMode: true, + reactCompiler: {target: '18'}, })
If you decide to give the React Compiler a try make sure you share your feedback and experience using it with the Working Group!
Sanity Plugin authors can also ship plugins that are pre-compiled, if they're using
@sanity/pkg-utils
, by running:pnpm add react-compiler-runtime@beta && pnpm add --save-dev @sanity/pkg-utils@latest babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta
yarn add react-compiler-runtime@beta && yarn add --save-dev @sanity/pkg-utils@latest babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta
npm install --save-exact react-compiler-runtime@beta && npm install --save-exact --save-dev @sanity/pkg-utils@latest babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta
and changing the
package.config.ts
to: