Fixes a bug that caused React 19 applications to fail during sku build
#1143
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
TL;DR: Babel's JSX runtime is incompatible with React 19. We've been using Babel's JSX runtime since before React had one. React's runtime effectively makes the Babel one obsolete, so the plugin that injects it has been removed in favour of letting
@babel/preset-react
inject React's runtime.Caution
This PR does not explicitly implement support for React 19 or any of its features (e.g. RSC, the React compiler).
While your application may work with React 19, it is not recommended to upgrade just yet.
Problem
Applications that attempt to run a
sku build
with React 19 as a dependency will run into the following error during the static render:This error occurs regardless of what is being rendered. My minimal reproduction involved rendering a single
<div />
.Investigation
There were a number of stepping stones that lead me to find the cause of this problem. The first notable observation was that this error occurred during
sku build
, not duringsku start
. So something about the production environment and/or the lack of a dev server was the cause.After some googling, I also found facebook/react#31832, which suggested that the issue was related to the JSX runtime somehow. Additionally, replacing the JSX
<div />
with acreateElement('div')
in the static render resulted in a successful build. However, serving the production app withsku serve
would result in Minified React error #525:This adds to the theory that the JSX runtime is somehow to blame.
After this I wanted to inspect the actual code being used for the static render, so I looked at the
dist/render.js
file, but there weren't any notable differences in the code between react 18 and react 19.Using RSDoctor to see the transform being applied by
babel-loader
, I noticed that the JSX runtime being injected into our code was actually babel's JSX runtime from@babel/runtime/helpers/jsx
rather than the React one fromreact/jsx-runtime
. This was being injected by@babel/plugin-transform-react-inline-elements
which notably only runs in production. Removing this plugin and instead relying on the react JSX runtime (injected by@babel/preset-react
) fixed the error.Cause
It's not immediately apparent why swapping from babel's JSX runtime to React's would fix the issue. Comparing the result of the JSX runtime on a simple
<div />
makes the issue more apparent:With a bit of digging, we can see that the symbol used to identify React elements was intentionally changed in React 19 to help make runtime mismatch errors more apparent. In reality the error we were seeing wasn't very helpful at all 😢.
Fix
The JSX runtime provided by
@babel/plugin-transform-react-inline-elements
evidently doesn't support React 19. Removing it results in babel injecting React's runtime (via
@babel/preset-react
) which supports React 19, and fixes the build error.We've likely just kept the babel runtime around because we were using it since well before React provided it's own JSX runtime. However, it's effectively obsolete thesedays, so I have no qualms with removing it.
Note
While I wanted to update a single fixture to use React 19 in order to reproduce the bug, due to various dependencies still not support React 19, dependency overrides would've been required to get this working.
I'd prefer to avoid overriding
react
dependencies, at least until we officially support React 19, so for that reason I chose to just implement the fix.