
Security News
The Next Open Source Security Race: Triage at Machine Speed
Claude Opus 4.6 has uncovered more than 500 open source vulnerabilities, raising new considerations for disclosure, triage, and patching at scale.
@softarc/native-federation
Advanced tools
Native Federation is a "browser-native" implementation of the successful mental model behind wepback Module Federation for building Micro Frontends and plugin-based solutions. It can be **used with any framework and build tool** for implementing **Micro F
Native Federation is a "browser-native" implementation of the successful mental model behind wepback Module Federation for building Micro Frontends and plugin-based solutions. It can be used with any framework and build tool for implementing Micro Frontends and plugin-based architectures.
This library allows to augment your build process, to configure hosts (Micro Frontend shells) and remotes (Micro Frontends), and to load remotes at runtime.
While this core library can be used with any framework and build tool, there is a higher level API on top of it. It hooks into the Angular CLI and provides a builder and schematics:

Please find the Angular-based version here.
Please find the vite plugin here.
Also, other higher level abstractions on top of this core library are possible.
The underlying mental model allows for runtime integration: Loading a part of a separately built and deployed application into your's. This is needed for Micro Frontend architectures but also for plugin-based solutions.
For this, the mental model introduces several concepts:
Big thanks to:
npm i @softarc/native-federation
As Native Federation is tooling agnostic, we need an adapter to make it work with specific build tools. The package @softarc/native-federation-esbuild contains a simple adapter that uses esbuild:
npm i @softarc/native-federation-esbuild
In some situations, this builder also delegates to rollup. This is necessary b/c esbuild does not provide all features we need (yet). We hope to minimize the usage of rollup in the future.
You can also provide your own adapter by providing a function aligning with the BuildAdapter type.
Just call three helper methods provided by our federationBuilder in your build process to adjust it for Native Federation.
import * as esbuild from 'esbuild';
import * as path from 'path';
import * as fs from 'fs';
import { esBuildAdapter } from '@softarc/native-federation-esbuild';
import { federationBuilder } from '@softarc/native-federation/build';
const projectName = 'shell';
const tsConfig = 'tsconfig.json';
const outputPath = `dist/${projectName}`;
/*
* Step 1: Initialize Native Federation
*/
await federationBuilder.init({
options: {
workspaceRoot: path.join(__dirname, '..'),
outputPath,
tsConfig,
federationConfig: `${projectName}/federation.config.js`,
verbose: false,
},
/*
* As this core lib is tooling-agnostic, you
* need a simple adapter for your bundler.
* It's just a matter of one function.
*/
adapter: esBuildAdapter
});
/*
* Step 2: Trigger your build process
*
* You can use any tool for this. Here, we go
* with a very simple esbuild-based build.
*
* Just respect the externals in
* `federationBuilder.externals`.
*/
[...]
await esbuild.build({
[...]
external: federationBuilder.externals,
[...]
});
[...]
/*
* Step 3: Let the build method do the additional tasks
* for supporting Native Federation
*/
await federationBuilder.build();
The method federationBuilder.build bundles the shared and exposed parts of your app.
The withNativeFederation function sets up a configuration for your applications. This is an example configuration for a host:
The shareAll helper shares all your dependencies defined in your package.json. The package.json is look up as described above:
// shell/federation.config.js
const { withNativeFederation, shareAll } = require('@softarc/native-federation/build');
module.exports = withNativeFederation({
name: 'host',
shared: {
...shareAll({
singleton: true,
strictVersion: true,
requiredVersion: 'auto',
includeSecondaries: false,
}),
},
});
The options passed to shareAll are applied to all dependencies found in your package.json.
This might come in handy in an mono repo scenario and when doing some experiments/ trouble shooting.
Since v21.1 it's also possible to add overrides to the shareAll for specific packages.
// shell/federation.config.js
const { withNativeFederation, shareAll } = require('@softarc/native-federation/build');
module.exports = withNativeFederation({
name: 'host',
shared: {
...shareAll(
{
singleton: true,
strictVersion: true,
requiredVersion: 'auto',
},
{
overrides: {
'package-a/themes/xyz': {
singleton: true,
strictVersion: true,
requiredVersion: 'auto',
includeSecondaries: { skip: '@package-a/themes/xyz/*' },
build: 'package',
},
'package-b': {
singleton: false,
strictVersion: true,
requiredVersion: 'auto',
includeSecondaries: { skip: 'package-b/icons/*' },
build: 'package',
},
},
},
),
},
});
The helper function share adds some additional options for the shared dependencies:
shared: share({
"package-a": {
singleton: true,
strictVersion: true,
requiredVersion: 'auto',
includeSecondaries: true
},
[...]
})
The added options are requireVersion: 'auto' and includeSecondaries.
If you set requireVersion to 'auto', the helper takes the version defined in your package.json.
This helps to solve issues with not (fully) met peer dependencies and secondary entry points (see Pitfalls section below).
By default, it takes the package.json that is closest to the caller (normally the webpack.config.js). However, you can pass the path to an other package.json using the second optional parameter. Also, you need to define the shared libray within the node dependencies in your package.json.
Instead of setting requireVersion to auto time and again, you can also skip this option and call setInferVersion(true) before:
setInferVersion(true);
If set to true, all secondary entry points are added too. In the case of @angular/common this is also @angular/common/http, @angular/common/http/testing, @angular/common/testing, @angular/common/http/upgrade, and @angular/common/locales. This exhaustive list shows that using this option for @angular/common is not the best idea because normally, you don't need most of them.
includeSecondariesis true by default.
However, this option can come in handy for quick experiments or if you want to quickly share a package like @angular/material that comes with a myriad of secondary entry points.
Even if you share too much, Native Federation will only load the needed ones at runtime. However, please keep in mind that shared packages can not be tree-shaken.
To skip some secondary entry points, you can assign a configuration option instead of true:
shared: share({
"@angular/common": {
singleton: true,
strictVersion: true,
requiredVersion: 'auto',
includeSecondaries: {
skip: ['@angular/common/http/testing']
}
},
[...]
})
Since v21 it's also possible to resolve Glob exports by enabling the globResolve property:
shared: share({
"package-a": {
singleton: true,
strictVersion: true,
requiredVersion: "auto",
includeSecondaries: {resolveGlob: true}
},
[...]
})
This is disabled by default since it will create a bundle of every valid exported file it finds, Only use this feature in combination with ignoreUnusedDeps flag. If you want to specifically skip certain parts of the glob export, you can also use the wildcard in the skip section:
shared: share({
"package-a/themes/xyz": {
singleton: true,
strictVersion: true,
requiredVersion: "auto",
includeSecondaries: {skip: "package-a/themes/xyz/*", resolveGlob: true}
},
[...]
})
Finally, it's also possible to break out of the "removeUnusedDep" for a specific external if desired, for example when sharing a whole suite of external modules. This can be handy when you want to avoid the chance of cross-version secondary entrypoints being used by the different micro frontends. E.g. mfe1 uses @angular/core v20.1.0 and mfe2 uses @angular/core/rxjs-interop v20.0.8, then you might want to use consistent use of v20.1.0 so rxjs-interop should be exported by mfe1. The "keepAll" prop allows you to enforce this:
shared: share({
"@angular/core": {
singleton: true,
strictVersion: true,
requiredVersion: "auto",
includeSecondaries: {keepAll: true}
},
[...]
})
The API for configuring and using Native Federation is very similar to the one provided by our Module Federation plugin @angular-architects/module-federation. Hence, most the articles on it are also valid for Native Federation.
The shareAll-helper used here shares all dependencies found in your package.json. Hence, they only need to be loaded once (instead of once per remote and host). If you don't want to share all of them, you can opt-out of sharing by using the skip option:
module.exports = withNativeFederation({
[...]
// Don't share my-lib
skip: [
'my-lib'
]
[...]
}
Paths mapped in your tsconfig.json are shared by default too. While they are part of your (mono) repository, they are treaded like libraries:
{
"compilerOptions": {
[...]
"paths": {
"shared-lib": [
"libs/shared-lib/index.ts"
]
}
}
}
If you don't want to share (all of) them, put their names into the skip array (see above).
When configuring a remote, you can expose files that can be loaded into the shell at runtime:
const { withNativeFederation, shareAll } = require('@softarc/native-federation/build');
module.exports = withNativeFederation({
name: 'mfe1',
exposes: {
'./component': './mfe1/component',
},
shared: {
...shareAll({
singleton: true,
strictVersion: true,
requiredVersion: 'auto',
includeSecondaries: false,
}),
},
});
On startup, call the initFederation method. It takes a mapping between the names of remotes and their remoteEntry.json. This is a file containing meta data generated by the augmented build process (see above).
import { initFederation } from '@softarc/native-federation';
(async () => {
await initFederation({
mfe1: 'http://localhost:3001/remoteEntry.json',
});
await import('./app');
})();
You can also pass the name of a file with the key data about your remotes:
import { initFederation } from '@softarc/native-federation';
(async () => {
await initFederation('assets/manifest.json');
await import('./app');
})();
Following the ideas of our friends at Nrwl, we call such a file a manifest:
{
"mfe1": "http://localhost:3001/remoteEntry.json"
}
Manifests allow to adjust your application to different environments without any recompilation.
For initializing a remote, also call initFederation. If you don't plan to load further remotes into your remote, you don't need to pass any parameters:
import { initFederation } from '@softarc/native-federation';
(async () => {
await initFederation();
await import('./component');
})();
To load a remote, just call the loadRemoteModule function:
const module = await loadRemoteModule({
remoteName: 'mfe1',
exposedModule: './component',
});
If you know the type of the loaded module (perhaps you have a shared interface), you can use it as a type parameter:
const module = await loadRemoteModule<MyRemoteType>({
remoteName: 'mfe1',
exposedModule: './component',
});
This library uses Import Maps. As currently not all browsers support this emerging browser feature, we need a polyfill. We recommend the polyfill es-module-shims which has been developed for production use cases:
<script type="esms-options">
{
"shimMode": true,
"mapOverrides": true
}
</script>
<script src="https://ga.jspm.io/npm:es-module-shims@1.5.17/dist/es-module-shims.js"></script>
<script type="module-shim" src="main.js"></script>
The script with the type esms-options configures the polyfill. This library was built for shim mode. In this mode, the polyfill provides some additional features beyond the proposal for Import Maps. These features, for instance, allow for dynamically creating an import map after loading a first EcmaScript module. Native Federation uses this possibility.
To make the polyfill to load your EcmaScript modules (bundles) in shim mode, assign the type module-shim.
Native Federation uses Web Standards like EcmaScript Modules. Most libs and frameworks support them meanwhile. Unfortunately, React still uses CommonJS (und UMD). We do our best to convert these libs to EcmaScript Modules. In the case of React there are some challenges due to the dynamic way the React bundles use the exports object.
As the community is moving to EcmaScrpt Modules, we expect that these issues will vanish over time. In between, we provide some solutions for dealing with CommonJS-based libraries using exports in a dynamic way.
One of them is fileReplacemnts:
import { reactReplacements } from '@softarc/native-federation-esbuild/src/lib/react-replacements';
import { createEsBuildAdapter } from '@softarc/native-federation-esbuild';
[...]
createEsBuildAdapter({
plugins: [],
fileReplacements: reactReplacements.prod
})
Please note that the adapter comes with fileReplacements settings for React for both, dev mode and prod mode. For similar libraries you can add your own replacements. Also, using the compensateExports property, you can activate some additional logic for such libraries to make sure the exports are not lost
createEsBuildAdapter({
plugins: [],
fileReplacements: reactReplacements.prod,
compensateExports: [new RegExp('/my-lib/')],
});
The default value for compensateExports is [new RegExp('/react/')].
Find out more about our work including Micro Frontends and Module Federation but also about alternatives to these approaches in our blog.
In our Angular Architecture Workshop, we cover all these topics and far more. We provide different options and alternatives and show up their consequences.
FAQs
Native Federation is a "browser-native" implementation of the successful mental model behind wepback Module Federation for building Micro Frontends and plugin-based solutions. It can be **used with any framework and build tool** for implementing **Micro F
The npm package @softarc/native-federation receives a total of 37,979 weekly downloads. As such, @softarc/native-federation popularity was classified as popular.
We found that @softarc/native-federation demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Claude Opus 4.6 has uncovered more than 500 open source vulnerabilities, raising new considerations for disclosure, triage, and patching at scale.

Research
/Security News
Malicious dYdX client packages were published to npm and PyPI after a maintainer compromise, enabling wallet credential theft and remote code execution.

Security News
gem.coop is testing registry-level dependency cooldowns to limit exposure during the brief window when malicious gems are most likely to spread.