Monolithic Frontend to Microfrontend Architecture
A step-by-step guide to decomposing a 200K-line monolithic React app into 15 independent microfrontends with Module Federation.
Executive Summary
A large e-commerce company's monolithic React frontend had grown to 200K lines of code, with 20 teams causing daily merge conflicts and 2-hour build times. Using Webpack Module Federation and Single-SPA, they decomposed it into 15 independently deployable microfrontends over 10 months, reducing build time to 5 minutes and eliminating deployment conflicts. This guide covers service boundary identification, shared component strategies, and cross-microfrontend communication patterns.
Why Migrate from Monolithic Frontend
The React monolith had become the primary bottleneck for developer productivity. Twenty teams contributed to the same codebase, causing daily merge conflicts and deployment queues. A single team's bug could break the entire site, and build times had ballooned to 2 hours, killing developer iteration speed.
- → 2-hour build time (developers wasted 4 hours daily waiting for builds)
- → Daily merge conflicts across 20 teams (30 minutes resolution per developer)
- → Site-wide outages weekly (any team's bug could crash entire frontend)
- → Inability to adopt new frameworks (locked into React version from 2020)
Frontend Migration Readiness
The team spent 3 months preparing: building the microfrontend orchestration layer (Single-SPA), setting up Module Federation, and creating a shared component library. They also established contract testing (PACT) to prevent breaking changes between microfrontends.
- • Single-SPA root orchestration layer with routing
- • Webpack Module Federation configuration (shared dependencies)
- • Shared component library as separate microfrontend (design system)
- • Contract testing (PACT) between all microfrontends
- • CI/CD pipeline with independent deployments per microfrontend
- • Feature flags for gradual traffic migration
Monolithic Frontend Assessment
The monolith had 200K lines of React code, 500 components, and 50 routes. The biggest pain points were the checkout flow (touched 30 components) and the product catalog (high traffic, frequent changes). Technical debt included 100+ unused components and inconsistent state management (Redux, Context, local state mixed arbitrarily).
Technical Debt
- • 100+ unused components (dead code, 20% of bundle size)
- • Inconsistent state management (Redux + Context + local = bugs)
- • No clear component ownership (every team could change everything)
- • Mixed React versions (16.8 → 18.0 migration incomplete)
Risks
- • Inconsistent UI if shared components not properly isolated
- • Cross-microfrontend state synchronization failures (e.g., cart count)
- • Performance degradation from loading multiple microfrontends
- • Team resistance to new architecture and ownership model
Target Microfrontend Architecture
The target had 15 microfrontends organized by business capability: auth, product-catalog, cart, checkout, user-profile, orders, returns, reviews, search, recommendations, marketing-pages, help, admin, analytics, and shared-components. Each microfrontend had its own build pipeline, could be deployed independently, and was owned by a single team.
10-Month Frontend Migration Plan
Step 1: Phase 1: Foundation (Months 1-3)
Built Single-SPA root, Module Federation config, shared component library. Trained 20 teams on microfrontend patterns.
Step 2: Phase 2: Pilot Microfrontends (Month 4)
Extracted login and marketing pages as first microfrontends—low risk, independent. Proved architecture worked.
Step 3: Phase 3: High-Traffic Features (Months 5-7)
Extracted product catalog and search—high traffic but low interdependencies. Reduced bundle size 30%.
Step 4: Phase 4: Complex Features (Months 8-10)
Extracted checkout and cart—required cross-microfrontend state sync. Implemented event bus for communication.
Step 5: Phase 5: Decommission (Month 11)
Decommissioned monolith after 2 months of 100% traffic on microfrontends.
State Management Migration
The team used a shared Redux store initially for cross-microfrontend state (user session, cart count). They migrated to an event bus (BroadcastChannel API) for loose coupling. Each microfrontend maintained its own local state, with the event bus handling cross-domain communication.
- • Shared Redux store for global state (user, cart, notifications) during transition
- • Event bus (BroadcastChannel) for cross-microfrontend events after stabilization
- • Local state (useState, useReducer) for microfrontend-internal state only
- • Persisted state (localStorage) for user preferences across microfrontends
Common Microfrontend Migration Mistakes
Extracting microfrontends by technical layer (header, footer) instead of business capability
Impact: Microfrontends still coupled across domains (3x cross-team coordination)
Prevention: Use domain-driven design; extract by business capability (product, cart, checkout)
No shared component library
Impact: UI inconsistency across microfrontends (different button styles, fonts)
Prevention: Build shared component library as first microfrontend
Loading all microfrontends upfront
Impact: Initial bundle size 5MB (defeats purpose of decomposition)
Prevention: Lazy load microfrontends on route navigation
No contract testing
Impact: Breaking changes frequently crash unrelated microfrontends (weekly incidents)
Prevention: PACT contract tests for all cross-microfrontend APIs
Success Metrics After Microfrontend Migration
Who Should Lead Microfrontend Migration
Recommended Roles
Required Experience
- • Successfully led 1+ microfrontend migration of 10+ teams
- • Deep expertise in Webpack Module Federation and Single-SPA
- • Experience with contract testing (PACT)
- • Team leadership for 20+ frontend engineers
Related Roles
Frequently Asked Questions
- How do you handle shared state across microfrontends (e.g., cart count)?
- Use an event bus (BroadcastChannel API) for cross-microfrontend events. Each microfrontend maintains its own local state and listens to relevant events.
- What if microfrontends use different framework versions?
- Module Federation can share specific versions or allow multiple versions. Best practice: align versions across microfrontends via shared dependencies.
- How do you ensure consistent UI across microfrontends?
- Shared component library as separate microfrontend. All microfrontends import components from this library, ensuring visual consistency.