Module Federation setup
The shell uses @module-federation/vite (the official MF2 runtime plugin),
not @originjs/vite-plugin-federation. The MF2 plugin performs proper runtime
shared-version resolution so singletons (React, React DOM, Firebase) genuinely
stay single across the host and all remotes.
Mandatory build settings
- Build target
esnext— the plugin emits top-level await; any lower target breaks the build. - Async bootstrap boundary —
src/index.tsdoesimport('./bootstrap.tsx')so the shared scope is populated before any shared module is consumed.
Shared singletons
All shared singletons use the full object form (never the shorthand array). Both the shell host and every MFE remote must declare the exact same set:
shared: {
react: { singleton: true, requiredVersion: '^18.3.1', strictVersion: true },
'react-dom': { singleton: true, requiredVersion: '^18.3.1', strictVersion: true },
'react/jsx-runtime': { singleton: true, requiredVersion: '^18.3.1' },
'firebase/app': { singleton: true, requiredVersion: '^12.0.0' },
'firebase/auth': { singleton: true, requiredVersion: '^12.0.0' },
},
Every Firebase subpath used across the shell and a remote must be declared
separately — omitting one causes it to be bundled into the remote chunk, which
produces double Firebase initialisation and the
FirebaseApp named 'DEFAULT' already exists error.
Independent deployment
Remotes are referenced by URL. Each microfrontend exposes a single ./App
entry point (a MicrofrontendMount). A product team ships a new remote
remoteEntry.js to its own URL; the shell picks it up on the next page load
with no shell rebuild. The only coordination needed is when the SDK contract
changes in a breaking way — that is a semver'd API change.
Verifying no duplicated React
Use rollup-plugin-visualizer to confirm React is absent from remote chunks,
and pnpm why react react-dom firebase in each workspace package to confirm
the resolved versions before configuring requiredVersion ranges.