Learn to architect scalable micro-frontends with React and Module Federation. Discover how to manage shared dependencies and handle cross-app communication.
Previously in this course, we explored mastering modular directory structures and refactoring for scalability to organize monolithic codebases. While those techniques keep a single repo clean, they don't solve the "deployment bottleneck" where a single team's error can block the entire organization. Today, we shift toward Micro-frontends, an architecture that allows teams to develop, deploy, and scale application slices independently.
Micro-frontends extend the concept of microservices to the browser. Instead of one massive bundle, you partition the UI into separate, independently deployable applications that compose at runtime.
The most robust way to achieve this today is via Module Federation, a feature introduced in Webpack 5. Unlike older iframe-based approaches—which suffer from poor accessibility and limited state sharing—Module Federation allows applications to dynamically load code from other builds at runtime as if it were a local dependency.
Let's assume our running project requires a "Header" micro-frontend that can be updated independently of the main "Dashboard" application.
In the Header app's webpack.config.js, we expose the component:
JAVASCRIPTconst { ModuleFederationPlugin } = require(CE9178">'webpack').container; module.exports = { plugins: [ new ModuleFederationPlugin({ name: CE9178">'headerApp', filename: CE9178">'remoteEntry.js', // The manifest file exposes: { CE9178">'./Header': CE9178">'./src/components/Header', }, shared: { react: { singleton: true }, CE9178">'react-dom': { singleton: true } }, }), ], };
In the Dashboard app, we map the remote:
JAVASCRIPT// webpack.config.js new ModuleFederationPlugin({ name: CE9178">'dashboardApp', remotes: { headerApp: CE9178">'headerApp@http://localhost:3001/remoteEntry.js', }, shared: { react: { singleton: true }, CE9178">'react-dom': { singleton: true } }, });
We use React.lazy to import the remote component, wrapped in Suspense because the remote is fetched over the network:
JSXimport React, { Suspense } from CE9178">'react'; const RemoteHeader = React.lazy(() => import(CE9178">'headerApp/Header')); function App() { return ( <div> <Suspense fallback={<div>Loading Header...</div>}> <RemoteHeader /> </Suspense> <main>Dashboard Content</main> </div> ); }
A common pitfall is version mismatch. By setting singleton: true in your shared configuration, you force Webpack to use the highest version of a library found in the dependency tree across all federated apps, preventing the "multiple React instances" bug which breaks hooks.
For cross-app communication, avoid tight coupling via shared state stores like Redux. Instead, use:
window.dispatchEvent for loose coupling.| Strategy | Coupling | Deployment | Performance |
|---|---|---|---|
| Monolith | Tight | Atomic | Best (no network hops) |
| iFrames | Loose | Independent | Poor (heavy footprint) |
| Module Federation | Loose | Independent | Excellent (lazy loading) |
UserWidget component.UserWidget.remoteEntry.js being fetched when the page loads, followed by the specific chunk for the widget.userName prop from the Host to the Remote and verify it renders correctly.Micro-frontends using Module Federation provide the architectural scalability required for enterprise applications. By focusing on independent deployments, shared dependency management, and loose event-based communication, you can maintain a high-velocity development cycle without sacrificing stability.
Up next: We will secure our distributed architecture in Security Best Practices in React, covering how to protect sensitive data flow across micro-frontend boundaries.
Learn to unify React design patterns into a scalable architecture. Standardize component APIs and enforce team constraints to keep your codebase maintainable.
Read moreStop drowning in a sea of components. Learn to implement feature-based directory structures, use barrel files, and manage dependencies for scalable React apps.
Micro-Frontends with React
Advanced TypeScript with React