Skip to content

Module Federation

Module Federation enables runtime code sharing between micro frontends. Unlike build-time imports that bundle everything into a single artifact, Module Federation allows a host application to dynamically load code from remote applications at runtime—without redeploying the host when remotes change.

This guide explains what Module Federation is, how it works, and how to use it effectively in micro frontend architectures.

What Module Federation Is and Why It Matters

Section titled “What Module Federation Is and Why It Matters”

Module Federation was introduced in Webpack 5. It allows JavaScript applications to share code at runtime by exposing “remote” modules that a “host” application can consume as if they were local dependencies.

Why it matters for micro frontends:

  • Independent deployments — Team A deploys their micro frontend; the host picks it up automatically on next load. No host redeployment required.
  • Shared dependencies without duplication — React, shared UI components, and utilities can be loaded once and shared.
  • Runtime flexibility — Different environments or A/B tests can load different remote versions.

Module Federation fits into client-side composition: the host loads remote bundles dynamically and mounts them. For managing shared dependencies (React, state libraries), see shared dependencies.


Without Module Federation (or similar techniques):

  • Duplicate bundles: Each micro frontend bundles its own copy of React, lodash, etc., inflating total page size.
  • Tight coupling: Sharing code requires npm packages and coordinated releases.
  • Deployment coupling: Adding a new micro frontend or updating one requires rebuilding and redeploying the shell.

Module Federation lets you share code at runtime, negotiate versions automatically, and load remote applications without host redeployment.


  • Host: The shell or container that loads and orchestrates micro frontends. It consumes remotes.
  • Remote: A micro frontend that exposes modules for the host (or other remotes) to consume.

A single application can be both host and remote: it loads remotes and also exposes itself for others to load.

A remote exposes specific modules via a public API. The host imports them by name:

// Host imports from remote
const ProductCatalog = React.lazy(() =>
import('product_catalog/ProductCatalog')
);

The remote’s build produces a remoteEntry.js (or similar) that defines these exposed modules.

Module Federation can share dependencies between host and remotes. Instead of each app bundling React, the shared configuration ensures only one copy loads:

shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
}

singleton: true guarantees only one instance of the shared module exists across host and all remotes. Critical for React, Redux, and other libraries that assume a single instance.


Remote (product-catalog) webpack config:

product-catalog/webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'product_catalog',
filename: 'remoteEntry.js',
exposes: {
'./ProductCatalog': './src/ProductCatalog',
'./ProductDetail': './src/ProductDetail',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
},
}),
],
};

Host webpack config:

host/webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
product_catalog: 'product_catalog@https://cdn.example.com/product-catalog/remoteEntry.js',
checkout: 'checkout@https://cdn.example.com/checkout/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
},
}),
],
};
  1. remoteEntry.js — The remote builds a small entry file that registers its exposed modules in a global scope.
  2. Async loading — When the host executes import('product_catalog/ProductCatalog'), Webpack generates a dynamic import that fetches the remote’s remoteEntry (if not cached), then loads the requested chunk.
  3. Chunk splitting — Exposed modules and shared dependencies are split into separate chunks. Shared chunks load once and are reused.

Vite does not include Module Federation natively. The community plugin @originjs/vite-plugin-federation provides similar functionality:

vite.config.js
import federation from '@originjs/vite-plugin-federation';
export default {
plugins: [
federation({
name: 'product_catalog',
filename: 'remoteEntry.js',
exposes: {
'./ProductCatalog': './src/ProductCatalog.tsx',
},
shared: ['react', 'react-dom'],
}),
],
};

Rspack is a Rust-based bundler compatible with Webpack’s Module Federation plugin. Configuration is analogous to Webpack 5.


AspectRuntime (Module Federation)Build-Time (npm packages)
DeploymentIndependent per appCoordinated releases
Version negotiationAt runtimeAt build time
Bundle sizeShared code loaded onceMay duplicate if versions differ
ComplexityHigher (async loading, version resolution)Lower
FlexibilityHigh (A/B test remotes)Lower

Use Module Federation when deployment independence and runtime flexibility matter. Use build-time sharing when simplicity and tighter control are preferred.


Expose a design system or component library as a remote:

// design-system remote
exposes: {
'./Button': './src/Button',
'./Input': './src/Input',
'./Theme': './src/Theme',
}

Micro frontends import from the design system remote instead of bundling their own components.

Expose utilities for consistency across micro frontends:

exposes: {
'./utils': './src/utils',
'./api': './src/api',
}

A design system team maintains a remote; product teams consume it. Updates deploy independently—no need to bump package versions in every consumer.


If host expects React 18 and a remote bundles React 17, you may get hooks errors or duplicate React instances. Use shared with requiredVersion and strictVersion:

shared: {
react: {
singleton: true,
requiredVersion: '^18.0.0',
strictVersion: true, // Fail if version incompatible
},
}

Host loads Remote A, which loads Remote B, which loads the Host. Avoid circular remotes; use a shared kernel or event bus for cross-app communication instead.

Module Federation shares runtime code, not TypeScript types. Options:

  • Publish type definitions as a separate package
  • Use a monorepo with shared tsconfig paths
  • Define types in a @types package consumed by host and remotes

Remote (product-catalog):

webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
output: {
publicPath: 'https://cdn.example.com/product-catalog/',
},
plugins: [
new ModuleFederationPlugin({
name: 'product_catalog',
filename: 'remoteEntry.js',
exposes: {
'./App': './src/App',
},
shared: {
react: { singleton: true, requiredVersion: '^18.2.0' },
'react-dom': { singleton: true, requiredVersion: '^18.2.0' },
},
}),
],
};

Host:

webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
product_catalog: 'product_catalog@https://cdn.example.com/product-catalog/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.2.0' },
'react-dom': { singleton: true, requiredVersion: '^18.2.0' },
},
}),
],
};
// Host App.jsx
const ProductCatalogApp = React.lazy(() => import('product_catalog/App'));
function App() {
return (
<Suspense fallback={<Loading />}>
<ProductCatalogApp />
</Suspense>
);
}

Module Federation enables runtime code sharing between independently deployed micro frontends. Use it when deployment independence and shared dependencies matter. Configure host and remotes carefully, manage version alignment for singletons, and avoid circular dependencies.

For dependency strategies beyond Module Federation, see shared dependencies. For where Module Federation fits in the composition landscape, see composition strategies.

Go Deeper in the Book

This topic is covered in depth in Chapters 10-11 of Micro Frontends Architecture for Scalable Applications, with detailed examples, diagrams, and production-ready patterns.