Module Federation vs Native Federation for Angular
Both load separately deployed Angular code at runtime and share dependencies between host and remotes. The difference is what does the loading: a bundler runtime or the browser itself.
The same idea, two implementations
Federation lets a host application load code from separately built and deployed applications, called remotes, while the page is running. It also lets them share dependencies such as Angular itself, so the framework is downloaded and instantiated once.
Webpack Module Federation and Native Federation both do this. They share the vocabulary (host, remote, exposed module, shared dependency). They differ in the mechanism underneath.
How Webpack Module Federation loads remotes
Module Federation is a feature of Webpack 5, configured through ModuleFederationPlugin. Each remote build produces a small file, usually called remoteEntry.js. It describes which modules the remote exposes and which shared libraries it needs.
At runtime the host loads that file. Webpack's own runtime, which is bundled into each application, negotiates the shared dependency versions and then fetches the chunks for the requested module.
For Angular, the usual integration is @angular-architects/module-federation, which adds the plugin to an Angular CLI build through a custom Webpack configuration. Rspack, a Webpack-compatible bundler, supports Module Federation as well.
How Native Federation loads remotes
Native Federation (@angular-architects/native-federation) keeps the same mental model and builds it on browser standards. Remotes are built as ES modules. Shared dependencies are built as separate ES module bundles. Each remote publishes a metadata file, remoteEntry.json.
At startup the host reads that metadata and creates an import map. An import map tells the browser which URL to use for each package name. After that, loading a remote is a standard dynamic import().
A typical setup has a manifest that lists the remotes:
{
"billing": "https://billing.example.com/remoteEntry.json",
"catalog": "https://catalog.example.com/remoteEntry.json"
}
And a host route that loads an exposed module:
{
path: 'billing',
loadChildren: () =>
loadRemoteModule('billing', './routes')
.then(m => m.BILLING_ROUTES)
}
Because the mechanism does not depend on a bundler runtime, it works with Angular's esbuild-based application builder, the default for new projects since Angular 17. Setups often include the es-module-shims polyfill to cover gaps in how browsers handle import maps.
Build tooling implications
| Webpack Module Federation | Native Federation | |
|---|---|---|
| Bundler | Webpack 5 or Rspack | Bundler-agnostic; in Angular, the esbuild-based builder |
| Angular CLI fit | Needs the Webpack-based builder and a custom configuration | Works with the current default builder |
| Loading mechanism | Webpack runtime and remoteEntry.js | Import map and native import() |
| Shared dependency format | Handles CommonJS and ES module packages | Shared packages must work as ES modules; CommonJS-only libraries can need extra handling |
| Track record | Longer, with wide use outside Angular | Newer, from the authors of the Angular Module Federation plugin |
| Non-Angular remotes | Common, if they build with a compatible bundler | Possible for remotes that ship ES modules |
The direction of the Angular CLI matters here. The newer build system is based on esbuild and Vite, and new build features arrive there. The Webpack-based builder still works, but staying on it means staying on the older path. Builds with esbuild are also usually much faster, which every team feels in local development and in CI.
When to choose each
Choose Webpack Module Federation when:
- You already run a platform on it and it works. A stable setup is worth more than a newer mechanism.
- Your applications use older Angular versions that predate the esbuild-based builder.
- Your builds depend on Webpack-specific loaders or plugins.
- You must integrate remotes from other teams that already publish Module Federation containers.
Choose Native Federation when:
- You are starting a new platform on a current Angular version.
- You want to stay on the default Angular CLI builder and its faster builds.
- You prefer a mechanism based on standards over one tied to a bundler.
Moving from one to the other
The concepts map one to one, so a migration is mostly configuration work: the federation config, the bootstrap code of each application and the calls that load remotes. Plan three things:
- Order. The two runtimes do not share dependencies with each other by default. Mixing them on one page is possible with extra bridging work. Treat that as a transition state with an end date.
- Library compatibility. Check that every shared library is available as ES modules before you commit.
- Pipelines. The files that the host discovers change from
remoteEntry.jstoremoteEntry.json. Deployment and caching rules need the same update. See CI/CD for micro-frontends.
The loader is the replaceable part. The contracts around it last longer: which dependencies are shared and at which versions, who owns which routes, how remotes are discovered. Get those right and switching mechanisms later stays affordable.
Version rules are covered in Shared dependencies and versions. If you are also considering an orchestrator, read single-spa vs Module Federation.
More in this section
- single-spa vs Module Federation: how to choose
- Iframes, web components, packages or federation: integration techniques compared
- The micro-frontend host: what it is responsible for and what it must not do
- Routing across micro-frontends: route ownership, deep links and the navigation contract
Planning a migration or a micro-frontend platform?
In a 20-minute call you describe your situation and I tell you how I'd approach it. If it isn't worth doing yet, I'll say so.
Message me on LinkedIn