Module Federation 2.0: Independent Micro-Frontend Deployment
Module Federation is the pattern of loading separately built JavaScript bundles at runtime as if
they were ordinary modules. Version 1.0 proved the idea could work; it also cemented a reputation
for webpack-centric ergonomics, hand-maintained type stubs for remotes, and architectural debates
that confused “independent deployment” with “we do not like talking to the other team.” Module
Federation 2.0 reached stable in April 2026 with a broader mandate: meet teams where their bundler
already lives, treat Node.js as a first-class environment for SSR and backend-for-frontend services,
and narrow the TypeScript gap between host and remote with dynamic type generation instead of
copy-pasted .d.ts fiction.
This section is not a sales pitch for micro-frontends. Federation is powerful and expensive. Use it when the organizational topology demands runtime isolation; refuse it when a monorepo and shared packages already solve the problem.
What changed from 1.0 to 2.0
The headline improvements matter to TypeScript React shops in practical ways:
- Multi-bundler support across webpack, Rspack, Rollup, Rolldown, Rsbuild, Vite, and Metro. You are no longer locked into one toolchain to participate in a federated graph.
- Dynamic TypeScript types for remotes at development time, reducing the “unknown module” experience that made federated imports feel second-class in the editor.
- Node.js as a peer citizen for SSR and services, not only browser entry points.
mf-manifest.jsonas a deployment-oriented protocol for describing remote endpoints and capabilities.- Side Effect Scanner and related tooling aimed at predictable shared dependency
behavior—historically a source of subtle double-mount bugs when
sharedmisconfiguration allowed duplicate libraries.
None of that removes the core trade: you are pushing integration to runtime. You gain team autonomy; you pay in operational surface area, versioning discipline, and failure modes that only appear in production routing.
Host independence: what the shell should own
A recurring anti-pattern is treating the host application as a design system dictator—every remote must conform to the host’s component library, theme tokens, and layout opinions. That collapses the benefit of independent teams into a distributed monolith with extra network hops.
The Host Independence Principle reframes the host as a runtime platform, not a UI platform. The host should own:
- Top-level routing and navigation contracts
- Authentication session establishment and propagation
- Layout shell that is intentionally thin: chrome, not product
- Remote loading: resolving manifests, error boundaries around lazy boundaries, retries where appropriate
- Observability: tracing, error reporting context, correlation IDs
Each micro-frontend owns its UI implementation, including which component primitives it uses internally. Shared visual consistency, when required, is negotiated through tokens, contracts, or a thin shared package—not through the host reaching into remote internals.
That separation is cultural as much as technical. If product leadership still demands pixel-identical widgets across remotes, you will fight Federation with policy unless you invest in a real shared design system package consumed at build time.
Wiring host and remote
A host using Rsbuild might declare remotes that resolve through the manifest protocol:
// host — rsbuild.config.ts
import { pluginModuleFederation } from "@module-federation/rsbuild-plugin";
export default {
plugins: [
pluginModuleFederation({
name: "host",
remotes: {
checkout: "checkout@https://checkout.example.com/mf-manifest.json",
catalog: "catalog@https://catalog.example.com/mf-manifest.json",
},
}),
],
};
A remote built with Vite exposes concrete entry surfaces:
// remote — vite.config.ts
import { federation } from "@module-federation/vite";
export default {
plugins: [
federation({
name: "checkout",
filename: "remoteEntry.js",
exposes: {
"./CheckoutFlow": "./src/CheckoutFlow.tsx",
"./CartWidget": "./src/CartWidget.tsx",
},
shared: ["react", "react-dom"],
}),
],
};
On the host, treat remotes like lazy code—React.lazy plus Suspense keeps the user experience
honest when the network or CDN misbehaves:
const CheckoutFlow = React.lazy(() => import("checkout/CheckoutFlow"));
function App() {
return (
<Suspense fallback={<CheckoutSkeleton />}>
<CheckoutFlow />
</Suspense>
);
}
With MF 2.0’s type story, the editor can understand checkout/CheckoutFlow far more like a local
module than in the stringly-typed past—exact configuration depends on bundler plugin versions, but
the direction is clear: types should follow remotes, not live as orphaned declarations in the
host.
Federation versus monorepo: pick the real constraint
Monorepo fits when teams can share a repository, align on CI, and coordinate releases—even if
deployables differ. Shared packages, Turborepo graphs, and internal semver via workspace:* give
you compile-time safety and refactors that cross package lines.
Module Federation fits when organizational or compliance boundaries require independent repositories, separate deployment pipelines, or different release cadences that cannot be flattened without political cost. Federation composes artifacts at runtime; monorepos compose source at build time.
If your pain is “npm publish is annoying,” you do not need micro-frontends—you need better package release automation inside a monorepo. If your pain is “we legally cannot grant Team A access to Team B’s repo,” Federation is on the short list—along with iframes, web components, and edge-side includes—each with different security and UX trade-offs.
When not to use micro-frontends
Say no by default when:
- The team is small (commonly fewer than five engineers) on a single product. The coordination overhead dominates.
- Build-time sharing via packages meets integration needs without runtime loading.
- There are no hard organizational boundaries that require independent deployment; you only want “faster builds.” Splitting bundles and improving CI caching usually costs less than federating runtimes.
- You lack platform engineering capacity to operate manifests, version skew detection, shared dependency policies, and incident response across independently deployed assets.
Micro-frontends are a scaling tool for teams and delivery topology, not a performance trick. They can improve perceived velocity for org-chart reasons while making end-user performance worse if each remote ships redundant dependencies or waterfall loads.
Operating Federation responsibly
If you adopt MF 2.0, budget for:
- Contract tests between host and remotes—runtime integration without compile-time coupling still needs verification.
- Explicit shared dependency policy for
react, routers, and state libraries; ambiguity here recreates the duplicate React problem at distributed scale. - Graceful degradation when a remote fails; error boundaries and skeleton UIs are mandatory, not polish.
- Observability that tags spans with remote names so on-call engineers can tell which artifact regressed.
Module Federation 2.0 makes the mechanics fairer across bundlers and narrows the TypeScript gap. It does not remove the architectural question: are you composing products, or avoiding collaboration? Answer that honestly, then choose monorepo, federation, or a deliberate hybrid—with the lightest structure that fits.