Versions
@originjs/vite-plugin-federation: 1.4.1
vite: 8.0.1 (Rolldown bundler)
@vitejs/plugin-react: 4.3.4
tailwindcss: 4.2.2 with @tailwindcss/postcss
react: 19.2.4
What is Expected?
Remote apps should load correctly. The dynamicLoadingCss() function should receive an array of real CSS file paths (e.g., ["./style-abc123.css"]).
What is Happening?
With Vite 8 (which uses the Rolldown bundler), the generated remoteEntry.js calls dynamicLoadingCss() with a virtual module string instead of an array:
// Generated by vite-plugin-federation@1.4.1 + Vite 8:
"./App": () => (
a(`__v__css__/path/to/App.tsx`, false, `./App`),
o("./__federation_expose_App-XYZ.js").then(...)
)
But the dynamicLoadingCss function (a) does:
var a = (e, t, n) => {
// ...
e.forEach(e => { /* create <link> tag */ })
}
e is the __v__css__ string. String.prototype.forEach does not exist → TypeError: e.forEach is not a function is thrown.
This causes the entire remote module to fail to load, and the host app shows "Failed to load [remote]" via its ErrorBoundary.
Root Cause
Vite 8 uses Rolldown as its bundler. Rolldown represents CSS associated with a module using virtual module IDs prefixed with __v__css__. The federation plugin picks up these virtual IDs and passes them as-is to dynamicLoadingCss(), which expects an array of real relative file paths.
Reproduction
- Set up Module Federation with
@originjs/vite-plugin-federation@1.4.1 + vite@8.0.1
- Use Tailwind CSS v4 with
@tailwindcss/postcss in remote apps
- Build a remote app — inspect
dist/assets/remoteEntry.js
- Look for:
a(\v__css/path/to/Component.tsx`, false, `./App`)`
- Load the remote from the host — observe
TypeError: e.forEach is not a function
Workaround
Post-build, patch remoteEntry.js to replace the virtual module ID with the actual CSS bundle file reference:
// Before:
a(`__v__css__/path/to/App.tsx`, false, `./App`)
// After:
a(["./style-abc123.css"], false, `./App`)
Or as a Vite plugin in vite.config.ts:
function fixFederationCss(): Plugin {
return {
name: "fix-federation-css",
apply: "build",
closeBundle() {
const assetsDir = path.resolve(__dirname, "dist/assets");
const remoteEntry = path.join(assetsDir, "remoteEntry.js");
if (!fs.existsSync(remoteEntry)) return;
const cssFile = fs.readdirSync(assetsDir).find(f => /^style-.*\.css$/.test(f));
let code = fs.readFileSync(remoteEntry, "utf8");
const replacement = cssFile ? `a(["./${cssFile}"],` : "a([],";
code = code.replace(/a\(`__v__css__[^`]+`,/g, replacement);
fs.writeFileSync(remoteEntry, code);
},
};
}
Additional Notes
The fix should be applied in the plugin itself: before generating dynamicLoadingCss() calls, filter out virtual module IDs (starting with __v__css__) and resolve them to the actual CSS asset file in the build output.
Versions
@originjs/vite-plugin-federation: 1.4.1vite: 8.0.1 (Rolldown bundler)@vitejs/plugin-react: 4.3.4tailwindcss: 4.2.2 with@tailwindcss/postcssreact: 19.2.4What is Expected?
Remote apps should load correctly. The
dynamicLoadingCss()function should receive an array of real CSS file paths (e.g.,["./style-abc123.css"]).What is Happening?
With Vite 8 (which uses the Rolldown bundler), the generated
remoteEntry.jscallsdynamicLoadingCss()with a virtual module string instead of an array:But the
dynamicLoadingCssfunction (a) does:eis the__v__css__string.String.prototype.forEachdoes not exist →TypeError: e.forEach is not a functionis thrown.This causes the entire remote module to fail to load, and the host app shows "Failed to load [remote]" via its ErrorBoundary.
Root Cause
Vite 8 uses Rolldown as its bundler. Rolldown represents CSS associated with a module using virtual module IDs prefixed with
__v__css__. The federation plugin picks up these virtual IDs and passes them as-is todynamicLoadingCss(), which expects an array of real relative file paths.Reproduction
@originjs/vite-plugin-federation@1.4.1+vite@8.0.1@tailwindcss/postcssin remote appsdist/assets/remoteEntry.jsa(\v__css/path/to/Component.tsx`, false, `./App`)`TypeError: e.forEach is not a functionWorkaround
Post-build, patch
remoteEntry.jsto replace the virtual module ID with the actual CSS bundle file reference:Or as a Vite plugin in
vite.config.ts:Additional Notes
The fix should be applied in the plugin itself: before generating
dynamicLoadingCss()calls, filter out virtual module IDs (starting with__v__css__) and resolve them to the actual CSS asset file in the build output.