Micro frontends have become a popular approach for developing large-scale applications by splitting them into smaller, independent pieces. Each piece is a micro frontend that can be developed, tested, and deployed independently. In this article, we'll explore how to implement micro frontends in Angular using Webpack 5's Federated Modules.
Micro frontends are a way to extend the microservices pattern to the frontend. They allow teams to work on separate parts of a web application independently. Micro frontends are self-contained and provide their functionality as independent units that can be combined to form a complete application.
In a traditional single-page application (SPA), you might have one large Angular project with different modules and components. However, with a micro frontend architecture, the frontend is broken down into separate applications that communicate with each other. These applications can be developed and deployed by different teams using various technologies.
Webpack 5 introduced a powerful feature called Module Federation, which allows different applications to share modules at runtime. This is the backbone of implementing micro frontends in Angular. It enables the loading of shared components, services, or libraries across different applications without bundling them into each individual build.
The first step is to create the host Angular application that will load and manage the micro frontends.
ng new host-app --routing --style=scss
cd host-app
ng add @angular/elements
Once the host application is created, you will need to install the necessary Webpack packages.
npm install webpack webpack-cli webpack-dev-server --save-dev
Create a webpack.config.js file in the root of the project. This file will configure Module Federation.
const ModuleFederationPlugin = require('webpack').container.ModuleFederationPlugin;
const path = require('path');
module.exports = {
output: {
publicPath: 'auto',
},
resolve: {
extensions: ['.ts', '.js', '.html'],
},
plugins: [
new ModuleFederationPlugin({
name: 'hostApp',
remotes: {
microFrontendApp: 'microFrontendApp@http://localhost:4201/remoteEntry.js',
},
shared: ['@angular/core', '@angular/common', '@angular/router'],
}),
],
};
This configuration defines a hostApp that will load the remote microFrontendApp from the given URL.
Now, you need to create the micro frontend application.
ng new micro-frontend-app --routing --style=scss
cd micro-frontend-app
ng add @angular/elements
After the micro frontend is created, install Webpack and Module Federation plugin.
npm install webpack webpack-cli webpack-dev-server --save-dev
In the micro frontend app, create the webpack.config.js file. This will expose the module you want to share with the host app.
// webpack.config.js
const ModuleFederationPlugin = require('webpack').container.ModuleFederationPlugin;
const path = require('path');
module.exports = {
output: {
publicPath: 'auto',
},
resolve: {
extensions: ['.ts', '.js', '.html'],
},
plugins: [
new ModuleFederationPlugin({
name: 'microFrontendApp',
filename: 'remoteEntry.js',
exposes: {
'./AppComponent': './src/app/app.component.ts',
},
shared: ['@angular/core', '@angular/common', '@angular/router'],
}),
],
};
This configuration exposes the AppComponent from the micro frontend to be consumed by the host app.
You need to modify the Angular build and serve configurations to integrate Webpack. Update angular.json to include the custom Webpack config.
"projects": {
"host-app": {
"architect": {
"build": {
"options": {
"customWebpackConfig": {
"path": "./webpack.config.js"
}
}
}
}
},
"micro-frontend-app": {
"architect": {
"build": {
"options": {
"customWebpackConfig": {
"path": "./webpack.config.js"
}
}
}
}
}
}
In the host application, you can now consume the exposed module from the micro frontend. First, import the component from the remote app.
// app.component.ts in host app
import { Component, Injector, ViewContainerRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-root',
template: <div #microFrontendContainer></div>`,
})
export class AppComponent {
@ViewChild('microFrontendContainer', { read: ViewContainerRef })
container: ViewContainerRef;
constructor(private injector: Injector) {}
ngOnInit() {
import('microFrontendApp/AppComponent')
.then(({ AppComponent }) => {
const componentRef = this.container.createComponent(AppComponent);
})
.catch((err) => console.error('Error loading micro frontend:', err));
}
}
This code dynamically loads the AppComponent from the microFrontendApp and renders it inside the host app.
Finally, run both the host and micro frontend applications.
Serve the micro frontend application:
ng serve --port 4201
Serve the host application:
ng serve --port 4200
Now, you should be able to see the micro frontend loaded dynamically into the host application.
By using Webpack 5's Module Federation, we can implement a robust micro frontend architecture in Angular. Each application or module can be developed, tested, and deployed independently. This allows for better scalability, flexibility, and easier maintenance of large-scale applications.
In this guide, we demonstrated the steps to create a host and micro frontend application, expose components via Webpack Federation, and consume them in the host app. With this architecture in place, your Angular applications can leverage the power of micro frontends for greater modularity and team autonomy.