Building Micro-Frontends with Module Federation
Muhammad Umar Malik2 min read
My Module Federation Journey
About a year ago, my team faced a choice: rebuild our monolithic React app from scratch, or find a way to incrementally modernize it. We chose the latter, and Module Federation became the bridge.
The Problem with Monoliths
When your single React app grows past 50+ components and 30+ developers:
- Deployment bottlenecks — One small change requires a full rebuild and redeploy of the entire application
- Dependency conflicts — Different teams need different versions of the same packages
- Cognitive load — No one understands the full app; every change risks breaking unseen corners
Module Federation solves all three by letting each team own their own bundle.
How It Works
At its core, Module Federation is a runtime code-sharing mechanism. Here's the simplest configuration:
Host app (entry point):
// webpack.config.js
plugins: [
new ModuleFederationPlugin({
name: "host",
remotes: {
auth: "auth@http://localhost:3002/remoteEntry.js",
dashboard: "dashboard@http://localhost:3003/remoteEntry.js",
},
}),
];
Remote app (feature):
// webpack.config.js
plugins: [
new ModuleFederationPlugin({
name: "auth",
filename: "remoteEntry.js",
exposes: {
"./AuthButton": "./src/AuthButton.tsx",
},
shared: {
react: { eager: true, singleton: true },
"react-dom": { eager: true, singleton: true },
},
}),
];
Real-Win Benefits We've Seen
| Benefit | Before | After | | -------------------- | -------------------------- | ---------------- | | Feature deploy time | 45+ minutes | 5-8 minutes | | Duplicate React deps | 3× (18MB each) | 1× (6MB shared) | | Team independence | Blocked by merge conflicts | Fully autonomous | | Production incidents | 2-3/week | ~1/month |
The key insight: don't federate everything. Start with just the boundaries where teams need independence most.
Caveats & Lessons Learned
- TypeScript compatibility — Ensure all federated packages use the same
tsconfigsettings.isolatedModulesandstrict: truemust align across apps. - CSS isolation — Module Federation doesn't share CSS by default. We use
mini-css-extract-pluginwithexperimental.css: trueor extract shared styles to a global CSS bundle. - Version pinning —
sharedversions must match exactly. Usepackage.jsonenginesfield or atools.configfile to enforce. - Loading spinners — Remote components show a blank slot until loaded. Always provide a loading state or skeleton.
Getting Started Checklist
- [ ] Identify 1-2 bounded contexts to extract first
- [ ] Configure webpack Module Federation plugin in each app
- [ ] Agree on shared dependency versions across the org
- [ ] Set up type definitions for remote components
- [ ] Write integration tests for each remote-to-host handoff
- [ ] Monitor bundle size impact with
source-map-explorer
The learning curve is real, but the payoff in team autonomy is worth it. We went from "one deployment blocks everyone" to "each team ships daily without touching others' code."