Microfrontends in Angular 17 with Module Federation


What is a Microfrontend?

A Microfrontend is an architectural style where a frontend application is split into smaller, independently developed and deployed applications that work together seamlessly. It follows the same principle as microservices in the backend but applies it to frontend applications.


Key Concepts of Microfrontends

  1. Independent Development – Each microfrontend can be developed by a different team using its own framework or technology.
  2. Independent Deployment – Each microfrontend can be deployed separately without affecting the entire application.
  3. Runtime Integration – The microfrontends are loaded dynamically into a shell (host) application at runtime.
  4. Scalability – The architecture allows teams to scale and maintain applications more efficiently.

Why Use Microfrontends?

Better Team Autonomy – Teams can work independently on different parts of the application.
Faster Development & Deployment – Small updates can be released without deploying the whole app.
Technology Agnostic – Different parts of the frontend can be built using different frameworks (e.g., Angular, React, Vue).
Easier Maintenance – Smaller codebases are easier to manage and debug.

In this guide, we’ll set up a host application (shell) and multiple remote applications using Webpack 5’s Module Federation Plugin.

Step 1: Create the Applications

We’ll create three Angular applications:

  1. Shell (Host) – Main application that loads microfrontends
  2. Remote 1 – A microfrontend (e.g., mfe1)
  3. Remote 2 – Another microfrontend (e.g., mfe2)

Run the following commands:

# Install Angular CLI
npm install -g @angular/cli

# Create shell (host) application
ng new shell
cd shell
ng add @angular-architects/module-federation

# Create remote applications
cd ..
ng new mfe1
cd mfe1
ng add @angular-architects/module-federation

cd ..
ng new mfe2
cd mfe2
ng add @angular-architects/module-federation

Step 2: Configure Module Federation in mfe1 application

Remote App(mfe1) – webpack.config.js

const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');

module.exports = withModuleFederationPlugin({
  name: 'mfe1',
  filename: 'remoteEntry.js',
  exposes: {
    './Routes': './src/app/app.routes.ts', // ✅ Matches import in shell app
  },
  shared: shareAll({ singleton: true, strictVersion: false, requiredVersion: 'auto' }),
});

Step 3: Configure Microfrontend(mfe1) Applications

3.1 Create new component testing1 in mfe1

ng g c testing1

3.2 Create Standalone Route in mfe1

Modify src/app/app.routes.ts:

import { Routes } from '@angular/router';
import { Mfe1Component } from './testing1/testing1.component';

export const mfe1Routes: Routes = [
  {
    path: '',
    component: Testing1Component,
    title: 'MFE1',
    // standalone: true, // ✅ Required for standalone routing
  }
];

3.3 modify app.config.ts file in mfe1

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';

import { mfe1Routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(mfe1Routes)]
};

Step 4: Configure Module Federation in mfe2 application

Remote App(mfe2) – webpack.config.js

const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');

module.exports = withModuleFederationPlugin({
  name: 'mfe2',
  filename: 'remoteEntry.js',
  exposes: {
    './Routes': './src/app/app.routes.ts', // ✅ Matches import in shell app
  },
  shared: shareAll({ singleton: true, strictVersion: false, requiredVersion: 'auto' }),
});

Step 5: Configure Microfrontend(mfe2) Applications

5.1 Create new component testing2 in mfe2

ng g c testing2

5.2 Create Standalone Route in mfe2

Modify src/app/app.routes.ts:

import { Routes } from '@angular/router';
import { Mfe1Component } from './testing2/testing2.component';

export const mfe2Routes: Routes = [
  {
    path: '',
    component: Testing2Component,
    title: 'MFE2',
    // standalone: true, // ✅ Required for standalone routing
  }
];

5.3 modify app.config.ts file in mfe2

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';

import { mfe2Routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(mfe2Routes)]
};

Step 6: Configure the Shell Application

6.1 Modify webpack.config.js in shell

const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');

module.exports = withModuleFederationPlugin({
  remotes: {
    mfe1: 'mfe1@http://localhost:4201/remoteEntry.js',
    mfe2: 'mfe2@http://localhost:4202/remoteEntry.js',
  },
  shared: shareAll({ singleton: true, strictVersion: false, requiredVersion: 'auto' }),
});

6.2 Modify src/app/app.routes.ts in the shell app:

import { Routes } from '@angular/router';
import { loadRemoteModule } from '@angular-architects/module-federation';

export const routes: Routes = [
  {
    path: 'mfe1',
    loadChildren: () => loadRemoteModule({
      type: 'module',
      remoteEntry: 'http://localhost:4201/remoteEntry.js', // ✅ Ensure this URL is correct
      exposedModule: './Routes', // ✅ Must match `mfe1/webpack.config.js`
    }).then(m => m.mfe1Routes), // ✅ Must match exported name from mfe1.routes.ts
  },
  {
    path: 'mfe2',
    loadChildren: () => loadRemoteModule({
      type: 'module',
      remoteEntry: 'http://localhost:4202/remoteEntry.js',
      exposedModule: './Routes',
    }).then(m => m.mfe2Routes),
  },
  { path: '**', redirectTo: '' },
];

Step 7: Run the Applications

Modify angular.json to set different ports:

  • Shell → Port 4200
  • MFE1 → Port 4201
  • MFE2 → Port 4202

Start each application:

ng serve --port 4200   # Shell
ng serve --port 4201   # MFE1
ng serve --port 4202   # MFE2

Now, navigate to:

  • http://localhost:4200/mfe1 → Loads mfe1
  • http://localhost:4200/mfe2 → Loads mfe2

 

 

 

 

Download complete code here