Description
When a remote app uses React Compiler (babel-plugin-react-compiler), Rollup bundles react/compiler-runtime as a separate chunk. This chunk imports React via a direct static import:
import{r as R}from"./index-XYZ.js"; // direct React import
The plugin's AST transform in remote-production.ts converts import ... from 'react' → importShared('react'), but it only matches exact module names (line 326-327):
parsedOptions.prodShared.some(
(sharedInfo) => sharedInfo[0] === moduleName // exact match only
)
react/compiler-runtime is a sub-export of react — it doesn't match 'react' exactly, so it's never transformed. The result: the compiler-runtime chunk gets its own React instance (from the bundled ./index-XYZ.js), separate from the host's shared React.
At render time, the shared React has its hooks dispatcher initialized (__CLIENT_INTERNALS...H), but the compiler-runtime's isolated copy has H = null → crash:
TypeError: Cannot read properties of null (reading 'useMemoCache')
Impact
This only affects remote builds (apps with exposes). The host owns the React instance directly, so its compiler-runtime works fine.
Reproduction
- Host shares
react via federation
- Remote uses
babel-plugin-react-compiler
- Build remote →
compiler-runtime-*.js chunk has import{r}from"./index-*.js" (direct import, not importShared)
- Load remote in host →
useMemoCache crashes
Suggested fix
In the generateBundle hook of remote-production.ts, detect compiler-runtime chunks and rewrite them to obtain React through importShared("react") via the __federation_fn_import chunk:
// Before (direct import — isolated React)
import{r as R}from"./index-XYZ.js";
var r=R().__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
export function c(n){return r.H.useMemoCache(n)}
// After (shared React via importShared)
import{importShared as __s}from"./__federation_fn_import-XYZ.js";
var __react=await __s("react");
var __internals=__react.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
var __obj={c:function(n){return __internals.H.useMemoCache(n)}};
export{__obj as c};
The patch should only apply to remote builds (builderInfo.isRemote), since the host doesn't need it.
Environment
- vite-plugin-federation: 1.4.1
- React: 19.x
- babel-plugin-react-compiler: latest
- Vite: 7.x
Description
When a remote app uses React Compiler (
babel-plugin-react-compiler), Rollup bundlesreact/compiler-runtimeas a separate chunk. This chunk imports React via a direct static import:The plugin's AST transform in
remote-production.tsconvertsimport ... from 'react'→importShared('react'), but it only matches exact module names (line 326-327):react/compiler-runtimeis a sub-export ofreact— it doesn't match'react'exactly, so it's never transformed. The result: the compiler-runtime chunk gets its own React instance (from the bundled./index-XYZ.js), separate from the host's shared React.At render time, the shared React has its hooks dispatcher initialized (
__CLIENT_INTERNALS...H), but the compiler-runtime's isolated copy hasH = null→ crash:Impact
This only affects remote builds (apps with
exposes). The host owns the React instance directly, so its compiler-runtime works fine.Reproduction
reactvia federationbabel-plugin-react-compilercompiler-runtime-*.jschunk hasimport{r}from"./index-*.js"(direct import, notimportShared)useMemoCachecrashesSuggested fix
In the
generateBundlehook ofremote-production.ts, detectcompiler-runtimechunks and rewrite them to obtain React throughimportShared("react")via the__federation_fn_importchunk:The patch should only apply to remote builds (
builderInfo.isRemote), since the host doesn't need it.Environment