Skip to content

Module Federation Cheat Sheet

Module Federation lets webpack builds share runtime modules: a host consumes exposed modules from remotes loaded at runtime via remoteEntry.js.

How to use this Module Federation cheat sheet

The host webpack config lists remotes by name and URL; remotes expose modules in federation config. shared declares singleton dependencies (react, react-dom) with requiredVersion to dedupe across apps.

eager: true bundles shared deps in the entry chunk (host often eager for React); eager: false loads shared async. Version mismatches fall back or warn depending on singleton and strictVersion settings.

Quick Module Federation example

// webpack.config.js — remote
new ModuleFederationPlugin({
  name: 'shop',
  filename: 'remoteEntry.js',
  exposes: { './ProductPage': './src/ProductPage' },
  shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
});

// host
new ModuleFederationPlugin({
  name: 'shell',
  remotes: { shop: 'shop@https://cdn.example/shop/remoteEntry.js' },
  shared: { react: { singleton: true, eager: true }, 'react-dom': { singleton: true, eager: true } },
});

Host & Remote Roles

Role Example Notes
Host (shell) Consumes remote modules Owns page shell and routing
Remote Exposes modules via remoteEntry.js Deployed independently
name name: "shop" Unique federation scope
filename remoteEntry.js Entry script host loads at runtime
remotes shop@https://.../remoteEntry.js URL must be reachable from browser
exposes "./Button": "./src/Button" Public API surface of remote
Dynamic remote import("shop/ProductPage") Lazy load federated module
Runtime plugin Module Federation 2.0 enhanced API Advanced manifest and DTS sharing

Shared Dependencies

Option Example Notes
singleton react: { singleton: true } One instance across host and remotes
requiredVersion requiredVersion: "^18.2.0" Semver range for compatibility check
strictVersion strictVersion: true Fail load on mismatch instead of fallback
eager eager: true Include in initial bundle; no async shared chunk
import import: "react" Override shared module path
shareScope shareScope: "default" Isolate shared sets per scope
sharedStrategy loaded-first vs version-first MF2 runtime negotiation
Peer deps Align package.json peers across repos Prevent duplicate React

Versioning & Deployment

Practice Example Notes
Independent deploy Remote ships without host rebuild Host resolves remoteEntry URL at runtime
Version in URL /shop/v2/remoteEntry.js Pin host to known remote version
Latest alias /shop/latest/remoteEntry.js Convenience; risky without integration tests
Fallback remote Try v2 then v1 remote URL Graceful degradation on failed load
CI contract tests Host imports remote in e2e Catch breaking expose path changes
TypeScript Generate @types remotes or DTS federation Typed federated imports
SSR Node federation plugins Separate from browser remoteEntry flow
CDN cache Cache remoteEntry briefly; hash chunks long Balance deploy speed vs stale entry

Eager vs Async Loading

Pattern Example When
Eager shared on host react: { singleton: true, eager: true } Host provides React immediately
Async shared eager: false (default) Smaller initial host bundle
Eager remote expose Rare; usually lazy import Only for critical path widgets
Lazy route React.lazy(() => import("shop/ProductPage")) Code-split federated pages
Prefetch remoteEntry <link rel="prefetch" href="remoteEntry.js"> Warm connection before navigate
Error boundary Catch failed remote load Show retry when CDN or remote down
publicPath output.publicPath: "auto" Correct chunk URLs for remotes
Module type output.module + experiments.outputModule ESM federation in webpack 5

Lazy federated route in React

const ProductPage = React.lazy(() => import('shop/ProductPage'));

function Routes() {
  return (
    <Suspense fallback={<Spinner />}>
      <Route path="/products" element={<ProductPage />} />
    </Suspense>
  );
}

Wrap federated components in Suspense; network load happens on first navigation.

Dynamic remote at runtime

const remoteUrl = window.__REMOTES__.shop;
await __webpack_init_sharing__('default');
const container = await import(/* webpackIgnore: true */ remoteUrl);
await container.init(__webpack_share_scopes__.default);
const factory = await container.get('./ProductPage');
const Module = factory();

Advanced pattern for runtime-configured remote URLs (micro-frontend shell).

Nx module federation project.json stub

{
  "targets": {
    "serve": { "options": { "port": 4201 } }
  },
  "tags": ["scope:shop", "type:remote"]
}

Monorepo tools generate federation config; keep remote serve ports documented for local dev.

Common mistakes

  • Mismatching React versions across host and remotes with singleton disabled.
  • Hard-coding remoteEntry URL without environment-specific config for dev/staging/prod.
  • Exposing entire app root instead of narrow module APIs, coupling host to remote internals.
  • Caching remoteEntry.js aggressively while changing exposed module paths without coordination.

Key takeaways

  • Host consumes remotes; remotes expose modules via remoteEntry.js.
  • shared + singleton dedupes react and other libraries at runtime.
  • eager loads shared in entry; lazy import defers federated UI until needed.
  • Version remote URLs and run contract tests before promoting remote deploys.

Pro Tip

Treat exposed module paths as public API—semver them and never rename without a deprecation window and host compatibility shim.