A practical example of micro frontend architecture using Angular 21 and Native Federation. Features shared state management, signals, and independent deployable modules.
- Shell - Main host app that loads everything
- Dashboard MFE - Homepage with user stats
- Profile MFE - User profile viewer
- User MFE - Create new users
- @shared/user - Shared library for state management
All MFEs share the same UserService instance, so state stays in sync everywhere.
# Install dependencies
npm install
# Start all apps (4 separate terminals)
ng serve shell # http://localhost:4200
ng serve dashboard-mfe # http://localhost:4203
ng serve profile-mfe # http://localhost:4201
ng serve user-mfe # http://localhost:4202Open http://localhost:4200 and you're good to go!
npm install --save-dev concurrently
# Add to package.json:
"dev": "concurrently \"ng serve shell\" \"ng serve profile-mfe --port 4201\" \"ng serve user-mfe --port 4202\" \"ng serve dashboard-mfe --port 4203\""
# Then just:
npm run devEach MFE is a separate Angular app that gets loaded by the Shell at runtime. They all share:
- Common Angular dependencies (singleton mode)
- The
@shared/userlibrary for state management - Tailwind CSS for styling
flowchart TB
subgraph Browser["🌐 Browser (localhost:4200)"]
subgraph Shell["Shell (Host App)"]
Header[Header]
Footer[Footer]
Router[Router Outlet]
SharedLib["@shared/user (Singleton)<br/>UserService · User Model · Signals"]
end
subgraph Manifest["federation.manifest.json"]
M1["dashboardMfe → :4203/remoteEntry.json"]
M2["profileMfe → :4201/remoteEntry.json"]
M3["userMfe → :4202/remoteEntry.json"]
end
end
subgraph Dashboard["Dashboard MFE :4203"]
D1[remoteEntry.json]
D2["./Dashboard (exposed)"]
D3["Widgets: Stats, Users,<br/>Quick Actions, Info"]
end
subgraph Profile["Profile MFE :4201"]
P1[remoteEntry.json]
P2["./Profile (exposed)"]
P3[Profile View]
end
subgraph User["User MFE :4202"]
U1[remoteEntry.json]
U2["./CreateUser (exposed)"]
U3[Create User Form]
end
subgraph Shared["Shared Dependencies (singleton)"]
S1["@angular/core, @angular/common"]
S2["@shared/user (UserService)"]
S3["Other Angular packages"]
end
Router -->|"loadRemoteModule()"| Manifest
M1 --> D1
M2 --> P1
M3 --> U1
D1 --> D2 --> D3
P1 --> P2 --> P3
U1 --> U2 --> U3
Dashboard --> Shared
Profile --> Shared
User --> Shared
flowchart LR
subgraph MFEs["Micro Frontends"]
ProfileMFE[Profile MFE]
UserMFE[User MFE]
DashboardMFE[Dashboard MFE]
end
subgraph Service["UserService (Singleton)"]
US["Single Instance<br/>Shared State"]
end
subgraph Signals["Reactive Signals"]
S1["firstName()"]
S2["lastName()"]
S3["email()"]
S4["fullName()"]
end
ProfileMFE -->|"inject()"| US
UserMFE -->|"inject()"| US
DashboardMFE -->|"inject()"| US
US --> Signals
Signals -->|"Auto-sync UI"| MFEs
sequenceDiagram
participant User
participant Shell
participant Manifest as federation.manifest.json
participant MFE as Remote MFE
User->>Shell: Navigate to /profile
Shell->>Manifest: Lookup "profileMfe"
Manifest-->>Shell: http://localhost:4201/remoteEntry.json
Shell->>MFE: Fetch remoteEntry.json
MFE-->>Shell: Module metadata
Shell->>MFE: Load ./Profile component
MFE-->>Shell: Profile component
Shell->>User: Render Profile in router-outlet
- Angular 21 with standalone components
- Native Federation 21
- TypeScript (strict mode)
- Tailwind CSS
- Signals for reactive state
/- Dashboard/profile- User profile/user/create- Create user form/metadata- System status
MFE won't load?
Make sure all 4 apps are running. Check the ports match what's in federation.manifest.json.
State not syncing?
Clear browser cache and restart everything.