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
- Independent Development – Each microfrontend can be developed by a different team using its own framework or technology.
- Independent Deployment – Each microfrontend can be deployed separately without affecting the entire application.
- Runtime Integration – The microfrontends are loaded dynamically into a shell (host) application at runtime.
- 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:
- Shell (Host) – Main application that loads microfrontends
- Remote 1 – A microfrontend (e.g., mfe1)
- 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
→ Loadsmfe1
http://localhost:4200/mfe2
→ Loadsmfe2
Download complete code here