micro frontends

Micro frontends with Module Federation | Micro frontends architecture react | micro frontend react | module federation micro frontend react | module federation micro frontend | module federation react | module federation examples | module federation react example


πŸ’‘ What are Micro Frontends?

Micro front-end extends the concepts of microservices to the frontend world. Building a feature-rich, potent browser application, often known as a single-page app, that rests on top of a microservice architecture is now popular.
Using Micro Frontends, you can think of a website or web app as a collection of features owned by independent teams. Each team has a distinct area of business or mission it cares about and specializes in. A team is cross-functional and develops its features end-to-end, from database to user interface.

Over the past few years, IT companies have begun to break large software into smaller, easier-to-manage chunks. In this approach, many services can be developed, tested, and deployed independently. Each of them will then be implementable by different frontend teams, and even with different technologies. This ensures the same scalability, flexibility, and adaptability that comes with the backend microservice architecture.


πŸ’‘ Benefits of Choosing Micro Frontends Today

  1. Scaling up to multiple teams
  2. Adopting different technical stack
  3. Development and deployment become faster
  4. It makes your web application more maintainable
  5. Support code and style isolation.
  6. The testing becomes very simple.
  7. No need to touch the entire application for every small change.


πŸ’‘ What is Module Federation?

The implementation of the micro front-end concept at the frontend side required a lot of tricks in the past. Fortunately, Module Federation makes this task straightforward.
Module Federation allows loading Micro Frontends at runtime. It is a JavaScript architecture invented by Zack Jackson. This architecture allows the sharing of code and dependencies between two different application codebases.
Module Federation is an integrated part of webpack 5, and hence, it works with all modern web frameworks. Module Federation defines two roles: the host and the remote – to allow the loading of separately compiled and deployed micro frontends.

Module Federation plugin comes by default with Webpack 5 and we can easily import this with the below statement –

const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

Lets implement Module Federation plugin in React application

Create Two React applications, We have given names here ApplicationOne and ApplicationTwo.

micro frontends

application-one is running on http://localhost:3000/
application-two is running on http://localhost:3001/

πŸ’» Later, in the same article, I will explain, how can we deploy applications on different – different ports?.


βœ” application-one is the HOST application that contains a Login Component which we will expose for others.
βœ” application-two is a REMOTE application that will consume Login Component from application-one.

Note: The most important file is webpack.config.js where we will put all settings, loaders, rules, and plugins.


πŸ’‘ Install Webpack and html-webpack-plugin

Now Install the required Packages in both applications to work on Modular Federation.

> npm i webpack webpack-cli
> npm i html-webpack-plugin

"html-webpack-plugin": "^5.5.0", 
"webpack": "^5.73.0", // Please make sure, Webpack 5 is required for MF. "webpack-cli": "^4.10.0"

πŸ’‘ Loaders

We need to install loaders in both applications that are required for file transformations.

> npm install babel-loader css-loader style-loader

"babel-loader": "^8.2.5", 
"css-loader": "^6.7.1", 
"style-loader": "^3.3.1",


πŸ’‘ babel preset packages

babel-loader also needs some other package to run the transformation. So to install those packages, run the below npm command in both apps.

> npm install @babel/core @babel/preset-env @babel/preset-react

"@babel/core": "^7.18.6",
"@babel/preset-env": "^7.18.6",
"@babel/preset-react": "^7.18.6",


πŸ’‘ .babelrc
Now in both applications, Create a file named '.babelrc' & put the below object inside that. Webpack will automatically read this file.

{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ]
}

πŸ’‘ start and build command

In the Package.json file of each project, replace script: start and build with the below code.

"start": "webpack serve --open",
"build": "webpack build"


πŸ’‘ bootstrap.js and index.js

In both applications rename index.js file to bootstrap.js.
And then create a new index.js file and import bootstrap.js file here with the below line.
> If we do not perform this step, you will get a below line of error.
πŸ‘€ Uncaught Error: Shared module is not available for eager consumption: webpack/sharing/consume/default/react/react

import('./bootstrap.js')

webpack.config.js file of HOST application ‘application-one’

Create a Webpack.config.js on the root and put the following inside it.
const ModuleFederationPlugin =  require('webpack/lib/container/ModuleFederationPlugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const deps = require('./package.json').dependencies;
const path = require('path');

//  object { exposes?, filename?, library?, name?, remoteType?, remotes?, runtime?, shareScope?, shared? }
module.exports = {
    mode: 'development',
    devServer: {
        port: 3000, // App-one is running on port 3000
        hot: false,
        liveReload: true, // enable live reload
        open: true,
        headers: {
          "Access-Control-Allow-Origin": "*", // for CORS
        },
      },
    resolve: {
        extensions: ['.js', '.jsx']
    },
    module: {
        rules: [{
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader'
                }
            },
            {
                test: /\.css$/i,
                use: ['style-loader', 'css-loader'],
            }
        ]
    },
    plugins:[
        new ModuleFederationPlugin({
            name: 'applicationone', // HOST app name
            filename: 'remoteEntry.js', // remote file name
            remotes: {

            },
            exposes: {
                './Login' : './src/components/Login.js',
                './bootstrapCss': 'bootstrap/dist/css/bootstrap.min.css'
            },
            shared: {
                ...deps,
                react: {
                    singleton: true,
                    requiredVersion: deps.react,
                },
                'react-dom': {
                    singleton: true,
                    requiredVersion: deps['react-dom']
                }
            }
        }),
        new HtmlWebpackPlugin({ template: path.resolve(__dirname, "public", "index.html")})
    ]
}

filename: 'remoteEntry.js',

filename is the main file generated with webpack cli that contains all shared and exposed items and is used in remote applications with the below line.
‘applicationone@http://localhost:3000/remoteEntry.js’

exposes: {
             './Login' : './src/components/Login.js',
             './bootstrapCss': 'bootstrap/dist/css/bootstrap.min.css'
          },

We have to use the Login component in another application so we exposed that component from the HOST application with expose object.
Application-one Login component is using Bootstrap UI library, so we also need to expose that CSS to render styles.

 shared: {
                ...deps,
                react: {
                    singleton: true,
                    requiredVersion: deps.react,
                },
                'react-dom': {
                    singleton: true,
                    requiredVersion: deps['react-dom']
                }
            }

Exposed components are React components and depend on react and react-dom packages. That’s why we shared those packages as well with an shared object like the above.

If you want to use Bootstrap UI Library components as well in another application then you need to put those packages in a shared object like the below code.

'bootstrap' : {
	singleton: true,
	requiredVersion: deps['react-dom']
},
'react-bootstrap' : {
	singleton: true,
	requiredVersion: deps['react-dom']
}

All set. Now run application-one by command > npm run start

Micro frontends with Module Federation
micro frontends

REMOTE APP – webpack.config.js

Create webpack.config.js file in application-two at root lavel.
const ModuleFederationPlugin =  require('webpack/lib/container/ModuleFederationPlugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const deps = require('./package.json').dependencies;
const path = require('path');

//  object { exposes?, filename?, library?, name?, remoteType?, remotes?, runtime?, shareScope?, shared? }
module.exports = {
    mode: 'development',
    devServer: {
        port: 3001, // here post is 3001
        hot: false,
        liveReload: true
      },
    resolve: {
        extensions: ['.js', '.jsx']
    },
    module: {
        rules: [{
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader'
                }
            },
            {
                test: /\.css$/i,
                use: ['style-loader', 'css-loader'],
            }
        ]
    },
    plugins:[
        new ModuleFederationPlugin({
            name: 'applicationtwo',
            filename: 'remoteEntryapplicationtwo.js',
            remotes: {
                applicationone: 'applicationone@http://localhost:3000/remoteEntry.js', // HOST FILE path
            },
            exposes: {
            },
            shared: {
                ...deps,
                react: {
                    singleton: true,
                    requiredVersion: deps.react,
                },
                'react-dom': {
                    singleton: true,
                    requiredVersion: deps['react-dom']
                }
            }
        }),
        new HtmlWebpackPlugin({ template: path.resolve(__dirname, "public", "index.html")})
    ]
}
  remotes: {
    applicationone: 'applicationone@http://localhost:3000/remoteEntry.js',
            },

This is remote object where we define remoteEntry.js file of the HOST application.
You can check that file in the browser as well with the URL –
http://localhost:3000/remoteEntry.js

 devServer: {
        port: 3001, // here post is 3001
        hot: false,
        liveReload: true
      },

With the devServer object, we can define the port number where we have to run our application. We can set liveReload: true for auto-update changes in the browser.

All set for application-two. Now run application-two by command > npm run start

Micro frontends with Module Federation
micro frontends


πŸ’‘ Sharing data between both applications

As Both applications are React applications so we can share data using props.

So, here we will enter our email & password on the Login page And will show that in the application-two component after clicking on submit button.

Login Component of Application-One

This component will take data by props and will emit data with props.
import React, { useState } from 'react'
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';

export default function Login(props) {
    const [email, setmail] = useState('');
    const [password, setPassword] = useState('');
    const handleClick = () => {
        props.emitData({email, password});
    }

    const onEmailChange = (e) => {
        setmail(e.target.value);
    }
    const onPasswordChange = (e) => {
        setPassword(e.target.value);
    }
    const styles = {
        marginTop: {
            marginTop: 20
        },
        box: {
            border: 'solid 1px gray',
            padding: 10
        }
    }
    return (
        <div className='container'>
            <h3>Login Page (From Application One)</h3>
            <Form style={styles.box}>

                <Form.Group classemail="mb-3" controlId="formBasicEmail">
                    <Form.Label>Email address</Form.Label>
                    <Form.Control type="email" placeholder="Enter email" value={email} onChange={onEmailChange} />
                    <Form.Text classemail="text-muted">
                        We'll never share your email with anyone else.
                    </Form.Text>
                </Form.Group>

                <Form.Group classemail="mb-3" controlId="formBasicPassword">
                    <Form.Label>Password</Form.Label>
                    <Form.Control type="password" placeholder="Password" value={password} onChange={onPasswordChange} />
                </Form.Group>
                <Button style={styles.marginTop} variant="primary" onClick={handleClick}>
                    Submit
                </Button>
            </Form>
        </div>

    )
}

Order component of Application two

import React, { Suspense, useState } from 'react'
const Login = React.lazy(() => import('applicationone/Login')) // Lazy load of host component

export default function Order() {
    const [loginData, setLoginData] = useState(null);
    const receivedDataFromLogin = (value) => {
        setLoginData(value)
    }
    return (
        <div>Order Component of application-two
            <Suspense fallback={<div>Something went wrong..</div>}>
                <Login emitData={receivedDataFromLogin}></Login>
            </Suspense>
            <div>
               {
                    loginData ?
                    <div style={{marginTop: 10}}>
                        <div>You have entered below details (Fetching from Login Component - Application One)</div>
                        <div>Your Email: {loginData.email}</div>
                       <div> Your Password: {loginData.password}</div>
                    </div>
                    :
                    ''
               }
            </div>
        </div>

    )
}

index.js file of application-two

We imported bootstrap css file of host app here.
import ('applicationone/bootstrapCss')
import ('./bootstrap.js');
Micro frontends with Module Federation
sharing data in module federation

πŸ’‘ You can find the complete project on GitHub –

https://github.com/msaxena25/-msaxena25-Modular-Federation-React

Read here for webpack configs: https://webpack.js.org/concepts/module-federation/

Micro frontends with Module Federation | Micro frontends architecture react | micro frontend react | module federation micro frontend react | module federation micro frontend | module federation react | module federation examples | module federation react example

Leave a Reply

Your email address will not be published.