Implementing Micro-frontends for Large Applications

Deep Singh
Bits and Pieces
Published in
7 min readApr 13, 2023

--

Micro-frontends are an exciting and futuristic approach to modern web development. If you are working on a complex/large application and not using micro-frontends, chances are you will be needing this technology soon.

Hi Folks! In this post, we are going to learn how to handle complex and large applications using micro-frontends. As the name suggests, the goal is to isolate application into smaller applications and integrate them all into final requirement. Sounds cool ? It is. Let’s explore the how part*.

Micro Frontends are small, self-contained web applications that share a common front-end codebase. They communicate with each other using a shared API. The front-end of the app is decomposed into individual, loosely coupled “micro apps” that can be built, tested, and deployed independently.

*it’s good to have basic knowledge of webpack implementation.

The Need and Benefits

Possibility of using different technologies
Angular among others. Development teams will work independently on each micro-frontend, designing self-contained applications that don’t depend on each other.

Scalability
The fact that an application is scalable allows us, in business terms, to be able to continue growing according to the needs of the project without losing the qualities that add value to it, adding new ones.

More maintainable and reduced code
Developers will be able to work on each part of the front-end in isolation, in smaller pieces than if they had to do it on a monolithic front-end.

Independence and autonomy
Each micro-frontend can be developed in parallel without depending on the others. Different parts of the host application can have different versions, and be tested and deployed individually.

💡 Using an open-source toolchain such as Bit for the implementation of micro frontends ensures you enjoy these benefits and also streamlines the entire procedure. By adopting a composable, modularity-first design for your components, and independently storing, testing, and documenting atomic units of UI instead of entire apps at once, your app scales better, and is infinitely more maintainable. This guide will show you how.

Learn more here:

The Architecture

The Integrations

By using this architecture pattern, we can organise groups of developers in our team that will be dealing with each division of the final application. The code generated in each micro-application can be stored in a multi-repos to have the micro-frontends completely isolated.

Let’s take the example of an over-simplified e-commerce web application. We will call the base (final) application as the container. The product list is implemented under the products application and the cart show is implemented under the cart application. Hence, we have three different codebases to work with.

Regarding the integration of each micro-application within the host application, we could use the following technical approaches:

  • Client-side integration: The browser handles the integration.
  • Server-side integration: The server handles the integration.
  • Build-time integration: The integration is carried out at compilation time.

Tools like Webpack Federation Module can help us working with our micro-frontends.

Webpack is code bundler for javascript. We can simplify its usage as below:

By the end of 2020, Zack Jackson released his masterpiece “Module Federation” as a plugin in Webpack 5. The Module Federation Plugin changed the world of Micro-frontends to a totally new level.

The Implementation

We will be implementing the the client-side or run time integrations in our example e-commerce implementation. This integration has the following advantages:
1. ProductList can be deployed independently at any time.
2. Different versions of ProductList can be deployed and container can decide which one to use.

The complex or more time consuming part is Tooling and Setup.

We will be leveraging the power of module federation provided by webpack 5. The implementation can be summarised in the following steps.

  1. Designate one web app as Host (Container) and others as Remote (Products and Cart).
  2. In a Remote, move applicable functionalities to make available for other web application. And those files can be exposed using Module Federation.
  3. In Host, import the required functionalities from Remote

The code bases for our three applications can reside under different repos or in the same repo under different folders. For simplifications, let’s go with the latter option.
Also, each application can have a different technology. In our case, we will go with React.js for all applications.

Let’s use create-react-app and create three different react applications namely- container, cart and products. The dependencies under package.json of these should look like the below:

Faker is being used to generate fake api data for the example app.

We will be adding webpack.config.js under each root folder and our final folder structure should look like the below:

webpack config for the first isolated app — the products:

const HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

module.exports = {
mode: "development",
devServer: {
port: 8081,
},
plugins: [
new ModuleFederationPlugin({
name: "products",
filename: "remoteEntry.js",
exposes: {
"./ProductsIndex": "./src/bootstrap",
},
shared: ["faker"],
}),
new HtmlWebpackPlugin({
template: "./public/index.html",
}),
],
};

webpack config for the second isolated app — the cart:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

module.exports = {
mode: 'development',
devServer: {
port: 8082,
},
plugins: [
new ModuleFederationPlugin({
name: 'cart',
filename: 'remoteEntry.js',
exposes: {
'./CartShow': './src/bootstrap',
},
shared: ['faker'],
}),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
};

webpack config for the base app — the container:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

module.exports = {
mode: 'development',
devServer: {
port: 8080,
},
plugins: [
new ModuleFederationPlugin({
name: 'container',
remotes: {
products: 'products@http://localhost:8081/remoteEntry.js',
cart: 'cart@http://localhost:8082/remoteEntry.js',
},
}),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
};

So what is happening here?

Our isolated apps, products and cart, are running respectively at ports 8081 and 8081. The container is at port 8080.

Each of our isolated apps , products and cart, expose the root application codebase named as ProductsIndex and CartShow respectively. These are the specific names we will be using in order to mount these apps under the container.

Now, the container can have the access to the isolated apps via filename property of the ModuleFederationPlugin under each app using the remote property like-

remotes: {
products: 'products@http://localhost:8081/remoteEntry.js',
cart: 'cart@http://localhost:8082/remoteEntry.js',
}

Inside the index.js of container we can mount the isolated apps as:

import { mount as productsMount } from 'products/ProductsIndex';
import { mount as cartMount } from 'cart/CartShow';

console.log('Container!');

productsMount(document.querySelector('#products-app'));
cartMount(document.querySelector('#cart-app'));

Where products-app and cart-app are Id selectors of HTML elements where these apps will be mounted.

That’s all for implementations!

The Migration: existing codebase to micro-frontend

It is the most sensible part: finding the budget to migrate a module that will be the same, but different in a way, is not a piece of cake. We can follow four different strategies to ease the process:

  1. We must make sure we use the latest react with version greater than 17. React v17 and greater will be equipped with the Webpack version 5 or more which has the features of Micro frontends.
  2. Choose the Type of Micro frontend integration that suits your application.
  3. Modify the webpack.config (mainly for run time integration) file for the expose and remote access functionality.
  4. Try to build global services(like Authentication, Notification etc) across the application, specific to the functionality or the part which you like to build it as micro frontend.

Even if you are not using MFE today in your project, it is a good idea to try out a POC or personal project around this concept.

Thanks for reading!

Build Micro frontends with reusable components

Bit’s open-source tool help 250,000+ devs to build apps with components.

Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.

Learn more

Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:

Micro-Frontends

Design System

Code-Sharing and reuse

Monorepo

--

--