Micro-Frontends: The Next Gen Way to build Web Apps
Tags: webdev, javascript, webpack, microfrontend
Published: 2021-10-24 06:08:51

In enterprise-level applications, _working on an application from a single code base turns out to be a massive head-ache_. For this purpose, **Micro-Frontend** architecture was born. Let's dive into the world of **Micro-Frontend** and discuss some of the nitty-gritty of following this approach.

![lets go](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hgw2gb908ckvv6zqgju5.gif)

## What is Micro-Frontend?

Let's check the definition a google search spits out:

_**Micro-frontend** architecture is a design approach in which a front-end app is decomposed into **individual**, **independent “micro-apps”** working loosely together._

There are two major parts of a **Micro-frontend** application:

-   **Container**
-   **Sub-Applications**

The **container** determines when _each of the **sub-application** should be displayed on the screen_. All _the business logic is handled by each of the **sub-applications**_.

![micro-frontend architechture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ug8k7knaejoglv7sd3ie.png)

## Why use Micro-Frontend?

There are numerous benefits of using **Micro-Frontend** architecture:

-   You can use different framework in each **sub-application**
-   Modification or even bugs in one **sub-application** has no effect on the other **sub-applications**
-   You can run **A/B Tests** easily to maximize customer conversions
-   Makes it _easier for teams to collaborate on a project_ (can be hosted as _separate repository_ for each **sub-app** or a **mono-repo**)
-   and many more

## Key Principles of Micro-Frontend

There are two strict requirements for this architecture:

1. The **micro apps** should work _absolutely independently of each other_, for example, the auth sub-app should not in any way rely on data from the product sub-app
2. The **micro apps** _can communicate with the **container**_, but the **communication should be kept as minimal as possible** and should be done in **as generic means as possible**. So, even if both the container and a sub-app uses the same framework, let's say React, they should not pass React components between them, but some generic functions and objects. This ensures that during a major refactor of either the **micro apps** or the **container**, we don't have to work on the other.

## Basic Micro-Frontend app

Ok, enough talk! Now it's time to get our hands dirty making a basic **Micro-frontend** app.

![its time](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ham60zltdpd97le6v8m9.gif)

Let's make three folders for:
- container
- cart
- products

We would use `faker` to generate fake data for the **cart** and **products**. To install the library, open the folder in the command prompt, initialize a node project using `npm init` and use `npm install faker`.

Implementing **Micro-Frontend** would be a nightmare for pretty much everyone, but luckily we have **Module Federation** plugin available with **webpack** which makes it a piece of cake. To install all **webpack** associated packages, use (in each folder):

```bash
npm install -D webpack webpack-cli html-webpack-plugin webpack-dev-server
```

Add `public/index.html` in all three folders

```html
<!-- cart -->
<div id="dev-cart"></div>
```

```html
<!-- products-->
<div id="dev-products"></div>
```

```html
<!-- container-->
<div id="product-list"></div>
<hr />
<div id="cart-items"></div>
```

Now setting up our `webpack.config.js`:

```js
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    mode: 'development',
    devServer: {
        port: 3000,
    },
    plugins: [
        new HTMLWebpackPlugin({
            template: path.resolve(__dirname, 'public', 'index.html'),
        })
    ],
};
```

Copy the `webpack.config.js` at the **root** of each folder, but make sure you have different **port numbers** for the dev server.

Now let's set up the `Module Federation`:

```js
// cart/webpack.config.js

// ...
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const packageJson = require('./package.json')

module.exports = {
    // ...
    plugins: [
        new ModuleFederationPlugin({
            name: 'cart', // name of the item being exposed (required in container)
            filename: 'remoteEntry.js', // name of the file
            exposes: {
                './Cart': './src/bootstrap' // actual file being exposed
            },
            shared: packageJson.dependencies, // to remove duplicate external modules loading in the container
        }),
        // ...
    ]
};
```

```js
// products/webpack.config.js

// ...
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const packageJson = require('./package.json')

module.exports = {
    // ...
    plugins: [
        new ModuleFederationPlugin({
            name: 'products', // name of the item being exposed (required in container)
            filename: 'remoteEntry.js', // name of the file
            exposes: {
                './ProductIndex': './src/bootstrap' // actual file being exposed
            },
            shared: packageJson.dependencies, // to remove duplicate external modules loading in the container
        }),
        // ...
    ]
};
```

```js
// container/webpack.config.js

// ...
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const packageJson = require('./package.json')

module.exports = {
    // ...
    plugins: [
        new ModuleFederationPlugin({
            name: 'container', 
            remotes: {
                products: 'products@http://localhost:8000/remoteEntry.js', // importing products file from port 8000
                cart: 'cart@http://localhost:8001/remoteEntry.js', // importing cart file from port 8001
            },
        }),
        // ...
    ]
};
```

With **webpack** now setup, we need to add our **cart**, **products** and **container**:

```js
// cart/src/bootstrap.js

import faker from 'faker'

const mount = (element) => {
  // generating fake data
  element.innerHTML = `
  <p>You have ${faker.datatype.number(10)} items in your cart</p>
`
}

const mountPt = document.querySelector('#dev-cart')
if (process.env.NODE_ENV === 'development' && mountPt) {
  mount(document.querySelector('#dev-cart'))
}

export { mount } // sharing generic mount function
```

```js
// products/src/bootstrap.js

import faker from 'faker'

const PRODUCTS_COUNT = 5

const mount = (element) => {
    // generating fake data
    let productsArr = []
    for (let i = 0; i < PRODUCTS_COUNT; i++) {
        const product = faker.commerce.productName();
        productsArr.push(`<div>${product}</div>\n`)
    }

    const products = productsArr.join('')
    element.innerHTML = products
}

const mountPt = document.querySelector('#dev-products')
if (process.env.NODE_ENV === 'development' && mountPt) {
    mount(mountPt)
}

export { mount } // sharing generic mount function
```

```js
// container/src/bootstrap.js

import { mount as mountProducts } from 'products/ProductIndex'
import { mount as mountCart } from 'cart/Cart'

mountProducts(document.querySelector('#product-list'))
mountCart(document.querySelector('#cart-items'))
```

Now finally create an `index.js` file in the `src` folder of each **sub-app** & **container**

```js
import('./bootstrap')
```

Creating this file with **dynamic import** is absolutely crucial as **webpack** _fails to import the external packages_ without it (since the packages are shared, so they behave differently).

Now your app is ready. You can add the following script in the `scripts` section of `package.json`:

```json
"scripts": {
    "dev": "webpack serve"
}
```

and call `npm run dev` to start up the webpack server

**Products** (port 8000)

![products](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zkab8x31di1y7l3pgeyy.PNG)

**Cart** (port 8001)

![cart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p7altsv9jsvdynltrqmi.PNG)

**Container** & the entire app (port 3000)

![container](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tfk2xxatlyaq23t26nh9.PNG)

# Wrapping Up

In this article, we went through the basics of **Micro-frontend** architecture. Hope it helps you in your development journey :)

I am currently working on a project utilizing **Micro-frontend** architecture, feel free to check it out:

{% github https://github.com/ruppysuppy/Crypto-Crowdfund no-readme %}

Finding **personal finance** too intimidating? Checkout my **Instagram** to become a [**Dollar Ninja**](https://www.instagram.com/the.dollar.ninja/)

# Thanks for reading

Need a **Top Rated Front-End Development Freelancer**? Contact me on [Upwork](https://www.upwork.com/o/profiles/users/~01c12e516ee1d35044/)

Want to see what I am working on? Check out my [GitHub](https://github.com/ruppysuppy)

I am a freelancer who will start off as a **Digital Nomad** in mid-2022. Want to catch the journey? Follow me on [Instagram](https://www.instagram.com/tapajyotib/)

Follow my blogs for **Weekly new Tidbits** on [Dev](https://dev.to/ruppysuppy)

**FAQ**

These are a few commonly asked questions I get. So, I hope this **FAQ** section solves your issues.

1. **I am a beginner, how should I learn Front-End Web Dev?**
   Look into the following articles:

    1. [Front End Development Roadmap](https://dev.to/ruppysuppy/front-end-developer-roadmap-zero-to-hero-4pkf)
    2. [Front End Project Ideas](https://dev.to/ruppysuppy/5-projects-to-master-front-end-development-57p)

2. **Would you mentor me?**
   Sorry, I am already under a lot of workload and would not have the time to mentor anyone.

3. **Would you like to collaborate on our site?**
   As mentioned in the _previous question_, I am in a time crunch, so I would have to pass on such opportunities.

**Connect to me on**

-   [LinkedIn](https://www.linkedin.com/in/tapajyoti-bose/)
-   [Portfolio](https://tapajyoti-bose.vercel.app)
-   [Upwork](https://www.upwork.com/o/profiles/users/~01c12e516ee1d35044/)
-   [GitHub](https://github.com/ruppysuppy)
-   [Instagram](https://www.instagram.com/tapajyotib/)