🌌 Angular CLI & Native Module Federation

Hi everyone, Micro-frontend is a massive subject. There is a lot I want to cover, but in this post, I want to keep things as simple as possible. I have created two new repositories to compare performance, scalability, security, clean code and build options between Nx and a standard Angular CLI setup (Only Native Module Federation). Let’s jump right into it!
Angular CLI with NX is a professional-grade monorepo demonstrating the future of Microfrontend (MFE) Architecture. It moves away from legacy Webpack-based Module Federation in favor of Native Federation, leveraging browser-standard Import Maps and high-performance Angular 21. Key benefit of Nx that stops you from rebuilding code that hasn't changed, making build times much faster. Also, a visual tool in Nx that helps you see and manage how different micro-frontends connect, keeping the project organized as it grows. Also, that helps manage complex MFE relationships, ensuring architectural integrity as the project scales.
🏗️ The Architecture
This project implements a Federated Runtime where the "Shell" (Host) and various "Planets" (currently Terra) are completely decoupled. To run the project locally, please ensure your Node.js version matches the one specified in the .nvmrc file, then run npm install to install all required packages.
Core Tech Stack
- Framework: Angular 21 (Experimental/Cutting-edge)
- Monorepo Tooling: Nx (Integrated Workspace)
- MFE Strategy: Native Federation (Import Maps & ESM)
- Node Version: v22.x (LTS)
🚀 Key Features
- Zero-Overhead Sharing: Uses
es-module-shimsto allow the browser to handle dependency resolution via Import Maps, avoiding redundant vendor chunks. - Decoupled Deployments: Remotes are discovered at runtime via a
federation.manifest.json. You can update a "Planet" without ever rebuilding the "Shell." - Task Orchestration: Replaced manual
concurrentlyscripts with Nx Task Pipelines, allowing for parallel execution and computation caching. - Strict Namespacing: Implemented a unique route-mapping strategy (
shellRoutes,terraRoutes) to prevent TypeScript declaration merging conflicts (TS2395).
🛠️ Technical Challenges & Solutions
1. Configuring Native Federation
Angular 21 supports Native Federation out of the box. We configure it in angular.json.
"architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"polyfills": ["zone.js"],
"esbuild": {
"native": true
},
"federation": {
"type": "dynamic",
"filename": "remoteEntry.js",
"exposes": {
"./Module": "apps/terra/src/app/app.component.ts"
},
"shared": {
"@angular/core": {
"singleton": true,
"strictVersion": true
},
"@angular/common": {
"singleton": true,
"strictVersion": true
}
}
}
}
}
}2. Dynamic Remote Discovery
Instead of hardcoding remote URLs, we use a federation.manifest.json that is discovered at runtime.
// apps/shell/src/federation.manifest.json
{
"terra": {
"url": "http://localhost:4201",
"type": "dynamic"
}
}3. Loading Remotes
We use the dynamic import() syntax with the federation helper.
// apps/shell/src/app/app.component.ts
import { loadRemoteModule } from '@angular/core/mf';
async loadTerra() {
const { TerraModule } = await loadRemoteModule({
type: 'dynamic',
remoteName: 'terra',
exposedModule: './Module'
});
}🧩 Key Concepts
1. Import Maps
Import Maps allow us to control how the browser resolves module specifiers. In our case, they map remote names to their URLs.
// Generated by Nx
{
"imports": {
"@nx-galaxy/terra": "http://localhost:4201/remoteEntry.js"
}
}2. Shared Dependencies
To avoid duplicate dependencies, we mark libraries as shared. The browser will load the dependency once and share it among all applications.
3. Strict Versioning
Setting strictVersion: true ensures that if two applications require different versions of a shared library, the build will fail, preventing runtime errors.
4. Service Injection
The architecture utilizes a custom inject method to facilitate communication and share shared instances between the Shell and remote micro-frontends. By passing services via this injection layer, ensure that the Host and Remote Apps remain decoupled while still allowing the remote apps to access core functionality provided by the Shell. It is very usefull way to provide a data from shell to stateless microfrontend apps.
🚀 Benefits
1. Zero Runtime Overhead
Unlike Webpack Module Federation, we don't need a custom runtime to handle module loading. The browser's native ES Module loader handles everything so don't worry about the polyfills.
2. True Decoupling
Remotes can be deployed independently of the shell. As long as the federation.manifest.json is updated, the shell will automatically discover the new remote.
3. Performance
- Import Maps eliminate the need for dynamic chunk loading.
- Shared Dependencies reduce bundle sizes.
4. Running the Application
# Start the shell
npm run start:shell
# Start the remote
npm run start:terraAngular CLI vs. with NX
In the evolving landscape of micro-frontends, the choice of tooling and architecture plays a pivotal role in scalability and developer experience. Today, we compare two approaches to building a federated application using Angular and Native Federation (a standard Angular CLI workspace) and nx-galaxy (an Nx-powered monorepo).
Project Overview
1. Standard Angular CLI
This project represents a comprehensive micro-frontend system. Angular CLI structure, managing all projects within a single angular.json file. Parallel execution is handled via the concurrently package.json with scripts. Managing multiple micro-frontends can be resource-intensive and difficult to orchestrate. Instead of relying on a messy concurrent execution script—such as concurrently ng serve shell ng serve app1 ... which consumes significant RAM and clutters the terminal, this architecture uses a more streamlined approach. Of course, with remote dev links and adding links to the federation.config.ts, it can save the day. Still, frontend is changing fast, so I think adapting to a new thing is more important than saving a day.
2. With NX
This project appears to be a migration or a streamlined version, currently containing 2 applications Shell and Terra.
It leverages Nx a powerful system, to manage the workspace. Configuration is decentralized, with each application having its own project.json simillar to webpack config.
Key Comparisons
| Feature | Standard Angular CLI | With NX |
|---|---|---|
| Workspace Structure | projects/ folder | apps/ and libs/ convention |
| Configuration | Monolithic angular.json (Centralized) | Modular project.json (Decentralized) |
| Task Execution | npm run serve:all (via concurrently) | npx nx run-many ... (Native Nx capability) |
| Federation Technology | @angular-architects/native-federation | @angular-architects/native-federation |
| Caching | Standard Angular build cache | Nx Computation Caching (Local & Remote) |
| Dependency Graph | Implicit / Mental Model | Visual nx graph |
Important Notes
The "Standard" Approach
The project shows how far you can get with just the Angular CLI.
- Pros: Familiar to any Angular developer. No extra tool learning curve.
- Cons: angular.json becomes massive and hard to merge. Running tasks across 6 apps requires manual script maintenance (e.g.,
"serve:all": "concurrently 'ng serve shell' 'ng serve terra' ...").
The Nx Approach (nx-galaxy)
The nx-galaxy project demonstrates the benefits of modern tooling.
- Pros: 3 main advantages on using Nx
- Config Split: Each app owns its config, reducing merge conflicts.
- Intelligent Execution:
nx run-manyschedules tasks efficiently. - Caching: If you built
terraand didn't change it, Nx replays the output instantly.
- Cons: Slight learning curve for Nx specific commands, especially on the build side.
Conclusion
While Angular CLI is a fully functional implementation with more micro-frontends included, Nx offers a superior architectural foundation.
For a production-grade micro-frontend system, my decide is migrating to Nx is a better choice. It solves the "monolithic configuration" problem and provides the task orchestration needed when your galaxy of micro-frontends grows from 6 to 60. Without Nx, the RAM costs these days will be high—especially since AI tools already consume so much memory. I tried to write this post as simplest way it possible and also In my next post, I’ll be diving into how to build microfrontend apps using this same approach!