There is a common misconception that "Module Federation" and "Webpack 5" are competing approaches to Microfrontends. In reality, Module Federation is the crown jewel of Webpack 5. The true architectural battle is between modern Run-Time Integration (Module Federation) and the legacy approaches we used to rely on: IFrames, Nginx routing, and Build-Time NPM composition. If you have spent years wrestling with frontend monoliths that take 20 minutes to build, this guide will show you how Webpack 5 changes the game.
What you'll learn
- Understand why traditional microfrontend patterns (NPM, IFrames) hit scaling walls
- Explain the mechanics of Webpack 5 Module Federation (Host vs. Remote)
- Configure shared dependencies to prevent loading React multiple times
- Design a resilient, federated UI architecture with boundary fallbacks
The Dark Ages: Pre-Webpack 5 Microfrontends
To appreciate Module Federation, you have to understand the pain it cures. When backend teams moved to microservices, frontend teams tried to follow suit. But browsers do not have the luxury of internal networks and Kubernetes DNS; everything must eventually execute in a single JavaScript runtime on the user's device.
Historically, we tried three flawed approaches:
- The IFrame: Pure isolation, but a nightmare for UX. Modals get clipped, CSS cannot be shared, and passing data requires clunky
postMessageAPIs. - Edge Routing (Nginx): Different paths (
/cart,/catalog) point to different independent apps. It works, but navigating between them causes hard page reloads, destroying the Single Page Application (SPA) experience. - Build-Time Integration (NPM Packages): Team A publishes the
Checkoutcomponent as an NPM package. Team B (the Host) installs it.- The fatal flaw: If Team A fixes a critical bug, Team B has to bump the version, rebuild the entire monolith, and redeploy. You haven't decoupled your deployments; you just decoupled your repos.
Enter Webpack 5 Module Federation
Module Federation allows a JavaScript application to dynamically load code from another application at run-time.
There is no NPM installation. There is no hard page reload. The Host app fetches the compiled JavaScript chunks over the network exactly when they are needed.
The Mental Model: Hosts and Remotes
In Module Federation, an application can be a Host (the shell that loads other apps), a Remote (the app being loaded), or Bidirectional (both a host and a remote).
A Remote application uses the ModuleFederationPlugin to expose specific files (like a React component or a utility function). Webpack compiles this exposed code into a special entry file (usually called remoteEntry.js). The Host application simply points to this file.
The Code: Wiring it up
Let's look at how two independent React applications configure Webpack 5 to federate a module.
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "cartApp", // The global variable name for the remote
filename: "remoteEntry.js", // The manifest file generated
exposes: {
"./MiniCart": "./src/components/MiniCart", // What we are sharing
},
shared: { react: { singleton: true }, "react-dom": { singleton: true } },
}),
],
};const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "hostApp",
remotes: {
// "cartApp" maps to the name in the remote. The URL is where it's hosted.
cart: "cartApp@https://cdn.mycompany.com/cart/remoteEntry.js",
},
shared: { react: { singleton: true }, "react-dom": { singleton: true } },
}),
],
};In the Host app's React code, using the Remote is as simple as a dynamic import. Webpack intercepts this import and fetches the chunk from the CDN.
import React, { Suspense } from "react";
// Webpack knows 'cart' is a federated module based on the config!
const MiniCart = React.lazy(() => import("cart/MiniCart"));
const App = () => (
<div>
<h1>Main E-Commerce Site</h1>
<Suspense fallback={<div>Loading Cart...</div>}>
<MiniCart />
</Suspense>
</div>
);The Dependency Dilemma: Shared Modules
If both the Host and the Remote use React, what happens when the Host loads the Remote? Do we download React twice? Does the browser crash because there are two instances of the React Virtual DOM?
This is where the shared configuration in the ModuleFederationPlugin shines.
By declaring shared: { react: { singleton: true } }, you tell Webpack: "If the Host has already loaded React, the Remote should just use the Host's copy. Do not download it again."
Strict Versioning
If you enforce a singleton, you must be careful with version mismatches. If the Host uses React 17 and the Remote demands React 18, Webpack will throw a runtime warning (or error, if strictly configured) because a singleton means only one version can win.
| Feature | NPM (Build-Time) | IFrames | Module Federation |
|---|---|---|---|
| Deployment Coupling | High (Requires Host rebuild) | Low (Independent) | Low (Independent) |
| UX & Performance | Excellent | Poor (Heavy DOM, disjointed) | Excellent (Native SPA) |
| Shared State/Dependencies | Easy (Bundled together) | Nearly impossible | Powerful via Shared scope |
| Complexity | Low | Low | High (Runtime orchestration) |
In Production
Microfrontends solve organizational scaling issues, but they introduce operational complexity. If a Remote goes down, your Host cannot crash.
In production
Always wrap federated modules in React Error Boundaries. If the CDN hosting cartApp is unreachable, React.lazy will throw an exception. An Error Boundary prevents the entire Shell from white-screening, allowing you to gracefully degrade (e.g., showing a "Cart unavailable" disabled button).
Performance
Be aggressive with your shared dependencies, but lazy with your loading. Don't fetch remoteEntry.js files in the <head> of your document. Let Webpack inject the script tags dynamically when the user actually navigates to the feature that requires them.
Production readiness
Test your understanding
Test your knowledge
0/1 answered1.What is the primary advantage of Webpack 5 Module Federation over publishing Microfrontends as NPM packages?
Interview questions
Interview questions
Key takeaways
- 1Module Federation is a runtime integration pattern, vastly superior to build-time NPM integration.
- 2Hosts consume modules; Remotes expose modules. An app can be both.
- 3The 'shared' configuration prevents bloated bundles and duplicate singletons (like React or Vue).
- 4With great power comes great responsibility: you must design for network failures and version mismatches.