Understanding Micro Frontends With a Hands-On Example Using React, Webpack 5, and Module Federation
Micro Frontends have been growing, recently, because of the need to break the Frontend monolith and the success showed in Microservices architecture. However, very few understand the concept or know that it exists. Reading about theoretical concepts makes things harder than they are. Therefore, I decided to explain the concepts with a hands-on project. I love to see things working to understand challenges and how things work. I will be using Webpack 5 and Module Federation Plugin. I hope this is useful for you. As usual, please send me your feedback.
“Good frontend development is hard. Scaling frontend development so that many teams can work simultaneously on a large and complex product is even harder.” Martin Fowler
Setup The Development Environment
Prepare your machine and install nodejs, if needed follow the article/video, below:
[Optional] You may create a Github project and clone it (I am using https://github.com/ranyelhousieny/micro-front-ends)
git clone https://github.com/ranyelhousieny/micro-front-ends.git
We will create the following 3 frontends container, micro-front-end-1, and micro-front-end-2
We will use create-react-app to create the 3 base projects as explained in the video, below, but we will call them container, micro-front-end-1, and micro-front-end-2, instead.
The container will orchestrate between multiple micro-front-ends
npx create-react-app container
npx create-react-app micro-front-end-1
npx create-react-app micro-front-end-2
Install dependencies for micro-front-end-1
cd micro-front-end-1
npm install webpack webpack-cli webpack-server html-webpack-plugin
npm install webpack webpack-cli webpack-server html-webpack-plugin webpack-dev-server
Implement micro-front-end-1
go inside the micro-front-end-1 directory and open the IDE using (code .)
Navigate inside src/index.js
remove all the code in index.js and replace it with
console.log(“micro-front-end-1”);
Use Webpack to bundle the code
A detailed explanation of Webpack configurations can be found in the following article
Let’s create a bundle using Webpack
Create webpack.config.js on the root of the micro-front-end-1
touch webpack.config.js
Now, let’s try to build and see what happens. Run the following command:
npm webpack
You will get few errors. Let’s take one by one.
The first error is asking about the mode. Webpack needs to know which mode to run with to be able to bundle the dependencies, accordingly. Let’s use Development mode. In webpack.config.js add the following
module.exports = {
mode: 'development',
};
This will take care of the first error and tell Weboack to build in development mode. Now, build again and watch the folders on the left hand side. You will notice a destination folder named “dist” will be created.
This is the folder that will be generated by Webpack’s build and bundle process and this is the folder we will deploy to the S3 Bucket. What happened here is that Webpack took all the code we have index.js, bootstrap.js, App.js … and there dependencies and bundled them in main.js as you can see.
Now we do not see the mode error, but we still React and loader errors. Those errors because Webpack needs loaders to understand React. As you know Browser only understand CSS, HTML,, and JavaScript ES5 (for now). JSX and ES6+ will need a compiler. We use babel for that. I will show you how to add those loaders to the configuration but first let’s create a local server to see our demonstrate our website (This will be S3 configured as a website at the end)
add a script to package.json
A new file will be created dist/main.js
Add the Webpack server
To add a local server on a certain port (here, I used 8001), add the following lines to webpack.config.js
devServer: {
port: 8081,
},
Now, run the following command
yarn webpack serve
This will start a local web server on port 8081. However, if you navigate to https://localhost:8081, you will only find alist of files. This is because we did not configure index.html, yet. We will do this in the coming step. However, try to browse main.js and you will be able to see its content as follows:
So far, Webpack created a local web server on Port 8081 as we configured it in Webpack.config.js. However, it is still missing a min component for the browser: index.html
HTML Webpack Plugin
We need to import html-webpack-plugin for Webpack to generate index.html and add the bundled js files to it.
const HtmlWebpackPlugin = require('html-webpack-plugin');
Also, add the html-webpack-plugin to fill the index.html as follows
webpack.config.js up to this step can be found here
Add serve to the script in package.json
remove everything in public/index.html and write the following
from the terminal under the directory of micro-front-end-1, start the script
npm run webpack
Go to the browser and browse HTTP://localhost:8081
right click and inspect to see the output of the console.log
This is the main concept.
Now, let’s display on the page, instead of console
Add the following to index.html
<div id=”root”></div>
Add the following querySelector
document.querySelector(‘#root’).innerHTML = `<h1>Micro-Front-End-1</h1>`
Refresh the browser
So far, we have one component and it is not connected to anything
=====================================
Now, let’s build the container
open another terminal and navigate to the root directory where we had the three apps
Now, navigate inside the container directory
Install dependencies as before.
install webpack webpack-cli webpack-server html-webpack-plugin
Now, let’s navigate back to the root of the three services and open VS code with the three components to work on them at the same time
Copy webpack.config.js from micro-front-end-1 to container and change the port to be 8080 as follows
Change the package.json script the same way we had before (you can copy it from micro-front-end-1
“webpack”: “webpack serve”,
Change index.js and index.html like before but write to the console “container”
run the script (npm run webpack) and check the browser http://localhost:8080/
========================
Connecting Both together
1- Add Module Federation Plugin
in micro-front-end-1/webpack.config.js add the following
This will expose index.js from micro-front-end-1. (Note that we used camelCase)
Now, let’s fetch it from the container
Go to container/webpack.config.js and add the following
container/webpck.config.js up to this point can be found here
now, rename container/src/index.js to container/src/bootstrap.js and import microFrontEnd1/MicroFrontEnd1Index (This is to run the file asynchronously until it gets the data from micro-front-end-1 to the container)
create a new container/src/index.js and import(‘./bootstrap’);
Now restart npm run webpack for both services
First, run micro-front-end-1
ctrl + c
npm run webpack
Second, run the container
ctrl + c
npm run webpack
Add the id to container/public/index.html
Browse the container port 8080 http://localhost:8080/ and you will find the text from micro-front-end-1 present there
Now, let’s understand what we have done.
As you are on the page, select Network, Disable Cache, and Java as shown below:
Refresh the page to send calls to servers and note the output
Right-click on the header next to Name and select Url from the menu to show the URL that has been called.
You will see the following URLs been calls
- It first called main.js on http://localhost:8080/main.js . This is the container
- Called the remoteEntry.js on http://localhost:8081/remoteEntry.js . This is micro-front-end-1
- Back again to the container calling bootstrap
- finally calling src_index_js.js from micro-front-end-1 (index.js) to present its output on the screen
All of this came from the ModuleFederationPlugin
webpack used this configuration from micro-front-end-1/webpack.config.js to create http://localhost:8081/remoteEntry.js and http://localhost:8081/src_index_js.js
Then the configuration on container/webpack.config.js told the server how to fetch http://localhost:8081/remoteEntry.js
inside http://localhost:8081/remoteEntry.js the information needed to fetch src_index_js.js
Module Federation Plugin allowed JavaScript to dynamically import code from micro-front-end-1 into the container at runtime.
==============
Adding the second Micro Frontend
Before adding the second Micro Frontend, let’s add more paragraphs to the first one. I like using a tool called Lorem ipsum
You just press F1 and write Lorem ipsum and select how many paragraphs
The new index.js for this step can be found here
Now, repeat the same processes inside micro-front-end-2
- Copy webpack.config.js from micro-front-end-1 to micro-front-end-2
- Update the names inside it as follows:
Install dependencies for micro-front-end-2
cd micro-front-end-2
npm install webpack webpack-cli webpack-server html-webpack-plugin
Add the webpack script to package.json
run npm install from inside micro-front-end-2 folder
Erase the content of micro-front-end-2/public/index.html and copy into it micro-front-end-1/public/index.html content. replace the id with a new id as follows
Erase the content of micro-front-end-2/src/index.js and copy into it micro-front-end-1/public/index.html content. replace the id with the one you had above and the name of the service in the <h1> tag as follows
Now, run npm run webpack
Now navigate to http://localhost:8082/
Now, connect it to the container
in container/webpack.config.js add another remote as follows
microFrontEnd2: ‘microFrontEnd2@http://localhost:8081/remoteEntry.js',
in container/src/bootstrab.js add the following import
import ‘microFrontEnd2/MicroFrontEnd2Index’;
in container/public/index.html add the id for micro-front-end-2 in the space you want. I added it as follows
restart webpack for the container and navigate to http://localhost:8080/
No, if you inspect the network traffic you will find calls to both servers
=================================
So far, we have not used React. In the following article, we will add a third micro frontend, react-microfrontend-3