Tutorial

Set up a complete Micro Frontend architecture with Native Federation from scratch. You'll create a host (shell) and a remote (Micro Frontend) that load shared dependencies at runtime.

Prerequisites

1. Clone the Starter

Start with the prepared starter branch:

git clone https://github.com/manfredsteyer/module-federation-plugin-example.git --branch nf-standalone-starter

cd module-federation-plugin-example

npm i

This repository contains two Angular applications: a shell (the host) and a Micro Frontend called mfe1 (the remote).

2. Install Native Federation

npm i @angular-architects/native-federation -D

3. Initialize the Remote

Configure mfe1 as a remote that exposes components:

ng g @angular-architects/native-federation:init --project mfe1 --port 4201 --type remote

4. Initialize the Host

Configure the shell as a dynamic host that reads remote configuration at runtime:

ng g @angular-architects/native-federation:init --project shell --port 4200 --type dynamic-host

A dynamic host reads configuration from a .json file at runtime. This allows you to change which remotes are loaded without rebuilding the application.

5. Host Configuration

The generated host configuration at projects/shell/federation.config.js:

const {
  withNativeFederation,
  shareAll,
} = require("@angular-architects/native-federation/config");

module.exports = withNativeFederation({
  name: "my-host",
  shared: {
    ...shareAll({
      singleton: true,
      strictVersion: true,
      requiredVersion: "auto",
    }),
  },
  skip: [
    "rxjs/ajax",
    "rxjs/fetch",
    "rxjs/testing",
    "rxjs/webSocket",
  ],
});

6. Remote Configuration

The generated remote configuration at projects/mfe1/federation.config.js:

const {
  withNativeFederation,
  shareAll,
} = require("@angular-architects/native-federation/config");

module.exports = withNativeFederation({
  name: "mfe1",
  exposes: {
    "./Component": "./projects/mfe1/src/app/app.component.ts",
  },
  shared: {
    ...shareAll({
      singleton: true,
      strictVersion: true,
      requiredVersion: "auto",
    }),
  },
  skip: [
    "rxjs/ajax",
    "rxjs/fetch",
    "rxjs/testing",
    "rxjs/webSocket",
  ],
});

7. Host Bootstrap

The generated projects/shell/src/main.ts initializes Native Federation before loading Angular:

import { initFederation } from "@angular-architects/native-federation";

initFederation("/assets/federation.manifest.json")
  .catch((err) => console.error(err))
  .then((_) => import("./bootstrap"))
  .catch((err) => console.error(err));

8. Federation Manifest

The manifest at projects/shell/src/assets/federation.manifest.json maps remote names to their entry points:

{
  "mfe1": "http://localhost:4201/remoteEntry.json"
}

Make sure this entry points to port 4201. Native Federation generates the remoteEntry.json automatically — it contains metadata about the remote.

9. Remote Bootstrap

The remote's projects/mfe1/src/main.ts also initializes federation:

import { initFederation } from "@angular-architects/native-federation";

initFederation()
  .catch((err) => console.error(err))
  .then((_) => import("./bootstrap"))
  .catch((err) => console.error(err));

10. Load the Remote

Add a lazy route to the shell's routing configuration at projects/shell/src/app/app.routes.ts:

import { loadRemoteModule } from "@angular-architects/native-federation";

export const APP_ROUTES: Routes = [
  {
    path: "",
    component: HomeComponent,
    pathMatch: "full",
  },
  {
    path: "flights",
    loadComponent: () =>
      loadRemoteModule("mfe1", "./Component").then(
        (m) => m.AppComponent
      ),
  },
  {
    path: "**",
    component: NotFoundComponent,
  },
];

11. Run the Application

Start the remote first:

ng serve mfe1 -o

Once it's running, start the shell in another terminal:

ng serve shell -o

Click the second menu item to load the remote component into the host. The Micro Frontend is loaded at runtime — the shell had no knowledge of it at build time.

What's Next?