Micro-Frontends & Module Federation
โฑ๏ธ ~4-minute bite ยท solve the sandbox to master
5-Year-Old Metaphor
โ The physical, real-world picture. No jargon.๐๏ธ City districts โ each team owns their district and deploys independently. Shared infrastructure (design system, auth, router) is the power grid.
Why micro-frontends exist
When a frontend codebase grows large enough, it hits scaling problems โ not technical, but organizational. A single repository with 50 engineers means merge conflicts, slow CI, coordination overhead on every release, and tight coupling between unrelated features. Micro-frontends are the frontend answer to microservices: let teams own their slice end-to-end and deploy independently.
Monolith frontend
- โข All teams commit to one repo
- โข One CI/CD pipeline
- โข Coordinated releases
- โข Shared tech stack
- โข Simple deployment
Micro-frontends
- โข Teams own separate repos
- โข Independent CI/CD per team
- โข Deploy any time
- โข Tech stack per team (risky)
- โข Complex composition
Shared infrastructure โ the power grid
Even with independent MFEs, teams share some infrastructure โ the design system, authentication service, analytics, and the shell app (router). This is analogous to city infrastructure: each district controls its own buildings, but shares roads, water, and power.
Communication patterns
Custom Events: MFE dispatches window.dispatchEvent(new CustomEvent('cart:added', { detail: item })). Shell or other MFEs listen. Decoupled but hard to type-check.
Shared state service: A tiny shared library (not React state) that each MFE can import. An event bus or observable. The "city radio" that all teams can broadcast on.
URL / query params: The most decoupled. MFE A navigates to a URL with params; MFE B reads those params. Works across page loads.
Interactive Sandbox
โ Move something, see it react instantly.Integration pattern
E-commerce app โ team ownership
Header MFE
Platform team
โ remote module
Product MFE
Catalog team
โ remote module
Cart MFE
Commerce team
โ remote module
Checkout MFE
Payments team
โ remote module
JS / CSS isolation level
Explicit shared config in webpack.config.js โ singleton:true for React to ensure one instance
Webpack 5 Module Federation โ micro-frontends expose JavaScript modules that other apps can consume at runtime. Each app can share dependencies (React, etc.) to avoid duplication.
Pros
+Runtime code sharing โ load remote components on demand
+Shared deps config โ one React instance across all MFEs
+Independent deployments โ remotes can update without host rebuild
+Bi-directional โ host can also expose modules to remotes
Cons
โWebpack-specific (Vite Federation plugin exists but less mature)
โVersion negotiation can be tricky (singleton: true required for React)
โType sharing requires extra tooling (@module-federation/typescript)
โBuild configuration complexity
Challenge
Explore all 5 micro-frontend patterns. For each, ask: can teams deploy independently? What happens when two MFEs need different React versions? What breaks across the boundary?
Why Should I Care?
โ The exact interview question + the bug it kills.Interview questions
Q: When does micro-frontend complexity pay off?
When: multiple teams (5+) work on the same UI, teams need to release on different schedules, teams have different tech stacks (legacy + new), or the monolith has become a deployment bottleneck. Not when: small team, single release cadence, or technical complexity would exceed the organizational benefit. A monorepo with good code ownership tooling (CODEOWNERS, Nx affected) often solves the same problems with less complexity.
Q: Module Federation vs npm packages โ key difference?
npm packages are resolved at build time โ when you install a package, its code is bundled into your output. If the package updates, you must reinstall and rebuild. Module Federation resolves at runtime โ the remote URL is fetched when the user loads your app. The remote can update (deploy a new version) without the host needing to rebuild. This is what enables true independent deployment.
Bug: Duplicate React instances breaking context
| 1 | // โ Host and remote both bundle React separately |
| 2 | // Host: react@18.2.0 (bundled) |
| 3 | // Remote: react@18.2.0 (bundled separately) |
| 4 | // Two React instances โ useContext returns undefined across boundary |
| 5 | ย |
| 6 | // โ Module Federation: share React as singleton |
| 7 | // webpack.config.js (host AND remote must both do this) |
| 8 | shared: { |
| 9 | react: { singleton: true, requiredVersion: '^18.0.0' }, |
| 10 | 'react-dom': { singleton: true, requiredVersion: '^18.0.0' }, |
| 11 | }, |
| 12 | // Now one React instance shared โ context works across MFEs |
The Deep Dive
โ Spec refs, engine internals, the minutiae.Webpack 5 Module Federation config
| 1 | // Remote (Product MFE) โ exposes its components |
| 2 | new ModuleFederationPlugin({ |
| 3 | name: 'productApp', |
| 4 | filename: 'remoteEntry.js', |
| 5 | exposes: { |
| 6 | './ProductCard': './src/components/ProductCard', |
| 7 | './ProductList': './src/components/ProductList', |
| 8 | }, |
| 9 | shared: { |
| 10 | react: { singleton: true, requiredVersion: '^18.0.0' }, |
| 11 | }, |
| 12 | }); |
| 13 | ย |
| 14 | // Host (Shell) โ loads the remote |
| 15 | new ModuleFederationPlugin({ |
| 16 | name: 'shell', |
| 17 | remotes: { |
| 18 | productApp: 'productApp@https://product.example.com/remoteEntry.js', |
| 19 | }, |
| 20 | shared: { react: { singleton: true } }, |
| 21 | }); |
| 22 | ย |
| 23 | // Usage in host |
| 24 | const ProductCard = React.lazy(() => import('productApp/ProductCard')); |
Testing micro-frontends in isolation
Each MFE should run standalone in development (with mocked shell APIs). Use a contract-testing approach: the shell publishes a contract (auth service API, custom event schema), and each MFE validates against it in CI. This prevents "works in isolation, breaks in the shell" problems.
| 1 | // MFE dev server: mock the shell's API surface |
| 2 | // src/mocks/shell.ts |
| 3 | window.__shell = { |
| 4 | auth: { user: { id: '123', name: 'Test User' } }, |
| 5 | navigate: (url) => console.log('Navigate to:', url), |
| 6 | analytics: { track: (event) => console.log('Track:', event) }, |
| 7 | }; |
| 8 | // MFE reads window.__shell in production โ shell injects real values |
Interview Questions
โ Real questions from real interviews โ with answers.They solve organisational scaling โ independent team deploys. They add complexity that isn't justified for small teams or single-release-cadence products.
npm packages are resolved at build time; Module Federation resolves remote modules at runtime โ the remote can deploy a new version without the host rebuilding.
Two React instances in one page cause 'Invalid hook call' errors because hooks rely on React's internal state that is instance-specific.
Custom events on window, a shared non-React event bus, or URL/query params โ all avoid direct imports across MFE boundaries.
Unit/component tests per MFE with mocked shell APIs; contract tests to validate the shell's API surface; E2E tests in the composed shell.
Memory Game
โ Quick quiz โ lock the concept in long-term memory.What is the key difference between iframe isolation and Module Federation isolation?