A comprehensive implementation of micro-frontend architecture using Webpack Module Federation, React, TypeScript, and Lerna for monorepo management.
- Overview
- Architecture
- Tech Stack
- Project Structure
- Data Sharing Strategy
- Lerna Monorepo Management
- Setup & Installation
- Running the Project
- How It Works
- Development Workflow
- Troubleshooting
This project demonstrates a micro-frontend architecture where multiple independent React applications work together as a unified system. Each micro-frontend can be developed, tested, and deployed independently while sharing components and state seamlessly.
- โ Independent Development: Each app can be developed separately
- โ Shared State Management: Global state synchronization across micro-frontends
- โ Module Federation: Runtime component sharing without build-time dependencies
- โ Monorepo Management: Unified development experience with Lerna
- โ TypeScript Support: Full type safety across all applications
- โ Hot Module Replacement: Fast development with instant updates
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CONTAINER APP (Port 3000) โ
โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โ
โ โ APP-1 Remote โ โ APP-2 Remote โ โ
โ โ (Port 3001) โ โ (Port 3002) โ โ
โ โ โ โ โ โ
โ โ CounterAppOne โ โ CounterAppTwo โ โ
โ โ Component โ โ Component โ โ
โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Global State: window.globalMicroState โ
โ { app1: number, app2: number } โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
| Application | Port | Role | Exposes | Consumes |
|---|---|---|---|---|
| Container | 3000 | Host/Shell | - | CounterAppOne, CounterAppTwo |
| App1 | 3001 | Remote | CounterAppOne | - |
| App2 | 3002 | Remote | CounterAppTwo | - |
- React 17.0.2: Frontend library for building user interfaces
- TypeScript 4.2.4: Type-safe JavaScript with static type checking
- Webpack 5.67.0: Module bundler with Module Federation plugin
- Lerna 3.22.1: Monorepo management tool
- Webpack Dev Server: Development server with hot reloading
- Babel: JavaScript transpiler for modern syntax support
- Fork TS Checker: TypeScript type checking in separate process
- Webpack Module Federation: Runtime code sharing between applications
- Dynamic Imports: Lazy loading of remote components
- Shared Dependencies: React and React-DOM shared as singletons
Microfrontend-ModuleFederation-React-Lerna/
โโโ ๐ฆ container/ # Host application (Port 3000)
โ โโโ src/
โ โ โโโ App.tsx # Main container component
โ โ โโโ bootstrap.tsx # App initialization
โ โ โโโ index.ts # Entry point
โ โโโ webpack.config.js # Webpack configuration
โ โโโ package.json # Dependencies & scripts
โ
โโโ ๐ฆ app1/ # Remote application 1 (Port 3001)
โ โโโ src/
โ โ โโโ components/
โ โ โ โโโ CounterAppOne.tsx # Exposed component
โ โ โโโ App.tsx # Standalone app
โ โ โโโ index.ts # Entry point
โ โโโ webpack.config.js # Module Federation config
โ โโโ package.json # Dependencies & scripts
โ
โโโ ๐ฆ app2/ # Remote application 2 (Port 3002)
โ โโโ src/
โ โ โโโ components/
โ โ โ โโโ CounterAppTwo.tsx # Exposed component
โ โ โโโ App.tsx # Standalone app
โ โ โโโ index.ts # Entry point
โ โโโ webpack.config.js # Module Federation config
โ โโโ package.json # Dependencies & scripts
โ
โโโ ๐ lerna.json # Lerna configuration
โโโ ๐ package.json # Root package.json
โโโ ๐ README.md # This file
The application uses a global window object approach for cross-micro-frontend state sharing:
// Global state interface
interface GlobalState {
app1: number; // Counter value for App1
app2: number; // Counter value for App2
}
// Initialize global state
if (!window.globalMicroState) {
window.globalMicroState = { app1: 0, app2: 1 };
}- Initialization: Each component reads initial state from
window.globalMicroState - Updates: When a user clicks a button, the component:
- Updates local React state
- Updates global window state
- Triggers re-render in all listening components
- Polling: Components poll global state every 100ms to detect changes
- Real-time Sync: Changes in one micro-frontend instantly reflect in others
| Approach | Pros | Cons | Used? |
|---|---|---|---|
| localStorage | Persistent | Domain-specific (localhost:3000 โ localhost:3001) | โ |
| postMessage | Cross-origin | Complex setup, iframe-based | โ |
| URL Parameters | Shareable | Manual copying required | โ |
| Global Window | Simple, same context | Only works in Module Federation | โ |
Lerna is a tool for managing JavaScript projects with multiple packages (monorepo). It optimizes the workflow around managing multi-package repositories.
# You'd need to run each app separately
cd container && npm start &
cd app1 && npm start &
cd app2 && npm start &# Single command starts all apps
npm start # Runs all 3 apps in parallel-
Dependency Management:
- Hoists common dependencies to root
- Reduces duplicate installations
- Manages inter-package dependencies
-
Script Orchestration:
{ "scripts": { "start": "lerna run --parallel start", "build": "lerna run build", "clean": "lerna run --parallel clean" } } -
Workspace Configuration:
{ "workspaces": { "packages": ["app1", "app2", "container"] } }
| Command | Description | Usage |
|---|---|---|
lerna bootstrap |
Install dependencies for all packages | Initial setup |
lerna run start |
Run start script in all packages | Development |
lerna run --parallel start |
Run start scripts in parallel | Faster development |
lerna clean |
Remove node_modules from all packages | Cleanup |
lerna version |
Version all packages | Release management |
- Node.js: Version 14+
- npm: Version 6+
- Git: For version control
-
Clone the Repository
git clone https://github.com/Chandan9898Kumar/Micro-Apps.git cd Microfrontend-ModuleFederation-React-Lerna -
Install Root Dependencies
npm install
This installs Lerna and sets up the monorepo structure.
-
Bootstrap All Packages (Optional - Lerna handles this automatically)
npx lerna bootstrap
-
Verify Installation
npx lerna list
Should show:
container,app1,app2
npm startThis command:
- Starts all 3 applications in parallel
- Opens browser automatically
- Enables hot module replacement
Access URLs:
- ๐ Container: http://localhost:3000 (Main application)
- ๐ฏ App1: http://localhost:3001 (Standalone Counter App 1)
- โก App2: http://localhost:3002 (Standalone Counter App 2)
# Terminal 1 - Container
cd container && npm start
# Terminal 2 - App1
cd app1 && npm start
# Terminal 3 - App2
cd app2 && npm start# Build all applications
npm run build
# Clean all node_modules
npm run clean
# Serve built applications
npm run servenew ModuleFederationPlugin({
name: "container",
remotes: {
app1: "app1@http://localhost:3001/remoteEntry.js",
app2: "app2@http://localhost:3002/remoteEntry.js",
},
shared: {
react: { singleton: true },
"react-dom": { singleton: true },
},
})new ModuleFederationPlugin({
name: "app1",
filename: "remoteEntry.js",
exposes: {
"./CounterAppOne": "./src/components/CounterAppOne",
},
shared: {
react: { singleton: true },
"react-dom": { singleton: true },
},
})- Container Starts: Loads at http://localhost:3000
- Dynamic Imports: Container dynamically imports remote components:
const CounterAppOne = React.lazy(() => import("app1/CounterAppOne")); const CounterAppTwo = React.lazy(() => import("app2/CounterAppTwo"));
- Runtime Resolution: Webpack fetches components from remote URLs
- Shared Dependencies: React/ReactDOM shared as singletons
- State Synchronization: Global state keeps all components in sync
User clicks button in Container
โ
Local state updates (setCount)
โ
Global state updates (window.globalMicroState)
โ
Polling detects change (100ms interval)
โ
All components re-render with new state
โ
UI updates across all micro-frontends
-
Create New Package:
mkdir app3 cd app3 npm init -y -
Add to Lerna Workspaces:
// Root package.json { "workspaces": { "packages": ["app1", "app2", "container", "app3"] } }
-
Configure Module Federation:
// app3/webpack.config.js new ModuleFederationPlugin({ name: "app3", filename: "remoteEntry.js", exposes: { "./CounterAppThree": "./src/components/CounterAppThree", }, })
-
Update Container:
// container/webpack.config.js remotes: { app1: "app1@http://localhost:3001/remoteEntry.js", app2: "app2@http://localhost:3002/remoteEntry.js", app3: "app3@http://localhost:3003/remoteEntry.js", // Add this }
- State Management: Use global state for shared data, local state for component-specific data
- Error Boundaries: Wrap remote components in error boundaries
- Loading States: Always provide fallback components for lazy-loaded remotes
- Type Safety: Share TypeScript interfaces between micro-frontends
- Testing: Test each micro-frontend independently and integration scenarios
Problem: Missing webpack installation
# Solution
npm install # Install root dependencies firstProblem: Remote application not running
# Solution
npm start # Ensure all apps are runningProblem: Remote entry point not accessible
# Check if remotes are running:
curl http://localhost:3001/remoteEntry.js
curl http://localhost:3002/remoteEntry.jsProblem: Global state not initialized
// Solution: Ensure global state is initialized in each component
if (!window.globalMicroState) {
window.globalMicroState = { app1: 0, app2: 1 };
}Problem: Lerna not installed globally
# Solution 1: Install globally
npm install -g lerna
# Solution 2: Use npx
npx lerna bootstrap- Shared Dependencies: Ensure React is shared as singleton
- Code Splitting: Use React.lazy for remote components
- Caching: Configure webpack caching for faster builds
- Bundle Analysis: Use webpack-bundle-analyzer to optimize bundles
- Network Tab: Check if remote entries are loading correctly
- Console Logs: Add logging to track state changes
- React DevTools: Debug component state and props
- Webpack Stats: Analyze bundle composition and dependencies
- Independent Deployment: Each micro-frontend can be deployed separately
- CDN Distribution: Host remote entries on CDN for better performance
- Version Management: Use semantic versioning for micro-frontend releases
- Rollback Strategy: Maintain multiple versions for quick rollbacks
- CORS Configuration: Properly configure cross-origin requests
- Content Security Policy: Set appropriate CSP headers
- Dependency Scanning: Regular security audits of dependencies
- Error Tracking: Monitor micro-frontend errors separately
- Performance Metrics: Track loading times and bundle sizes
- User Analytics: Measure user interactions across micro-frontends
- Fork the repository
- Create a feature branch:
git checkout -b feature/new-feature - Commit changes:
git commit -am 'Add new feature' - Push to branch:
git push origin feature/new-feature - Submit a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Happy Coding! ๐