Build Micro Frontends In React In 3 Easy Steps
Microfrontend is an approach to building applications where the front end is broken down into smaller, independent parts, each with its own user interface and functionality. These independent parts are then integrated to form a complete application.
This blog goes through the steps to implementing a React MFE architecture using Webpack Module Federation. It lists two alternatives: a component-based approach to MFEs and a traditional one.
The Component-Based Approach to MFEs
The MFEs architecture naturally emerges from component-driven development, as both aim to enable autonomous teams to develop loosely coupled and highly maintainable codebases and deliver features quickly and independently.
This section will use a Bit starter to generate full React MFEs solutions (implemented using Module Federation).
Run the following to install Bit:
npx @teambit/bvm install
Run the following to generate a Bit workspace with a complete component-based solution for React MFEs implemented using Module Federation:
bit new module-federation-react my-mf-workspace --env learnbit.module-federation/mf-react-env
🎊 Congratulations! You’ve successfully set up a component-based Micro Frontend (MFE) solution with Bit. 🎊
Since it is a component-based solution, it is implemented as a composition of independent Bit components. The host app, the remote app(s), the UI components, and even Webpack configurations — are all Bit components that can be maintained and delivered independent of a git repository or a project’s build setup.
To learn more about configuring your project and extending it with additional components and app components, read this blog:
Why use a component-based approach?
Since MFEs are often built using the same Bit components, the same “look and feel” is preserved across all apps. Changes to these components are automatically propagated to all MFEs, and can be easily tested and verified.
Bit components use reusable development environments for their development, build, and delivery. Using shared envs, the development and build process is consistent across all components and across all MFEs. Shared envs allow you to enjoy autonomous development while preserving the same level of quality and consistency across all components.
The Traditional Approach to MFEs
1. Setup React Microfrontend Project
We will use webpack module federation with React to build our first microfrontend.
- Create your microfrontend project directory. Let’s call it:
micro-host
. - Run below command to initiate project and install all dependencies:
npm init -y
npm install react react-dom --save
npm install @babel/core @babel/preset-env @babel/preset-react babel-loader css-loader html-webpack-plugin sass sass-loader style-loader webpack webpack-cli webpack-dev-server --save-dev
- Open
package.json
file and copy paste below npm scripts:
"scripts": {
"build": "webpack",
"start": "webpack serve --watch-files ./src"
}
- Create a file
babel.config.json
and paste below config:
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
- Create a file
webpack.config.js
and paste below config:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const deps = require("./package.json").dependencies;
module.exports = {
mode: "development",
resolve: {
extensions: [".css", ".scss", ".js", ".jsx"],
},
module: {
rules: [
{
test: /\.s?css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: {
url: {
filter: (url) => {
if (url.startsWith("data:")) {
return false;
}
return true;
},
},
},
},
"sass-loader",
],
},
{
test: /\.jsx?$/,
use: ["babel-loader"],
exclude: /node_modules/,
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: "asset/resource",
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "public", "index.html"),
}),
new ModuleFederationPlugin({
name: "FIRST_APP",
filename: "remoteEntry.js",
exposes: {
"./app": "./src/components/App",
},
}),
],
};
In above config we have named our microfrontend FIRST_APP
. We are exposing our App
component that can be integrated directly using it’s remote URL.
- Create a directory
public
and create filepublic/index.html
and paste below code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Micro App</title>
</head>
<body>
<div id="container"></div>
</body>
</html>
This file creates the structure for our React web application. The app will be rendered inside “container” div when you visit the app normally like you do with any other React application.
- Create a directory
src
and create filesrc/index.js
and paste below code:
import React from 'react';
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import MainApp from './components/Main';
const rootElement = document.getElementById("container");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<MainApp />
</StrictMode>
);
We are using React 18 here and the MainApp
component is created to use the application directly in web browser.
- Create a directory
src/components
and create a fileApp.js
and paste below code:
import * as React from 'react';
import "./styles.css";
export default function App({ onChange }) {
return (
<div className="MicroApp">
<h1>Micro App</h1>
<input onChange={onChange} type="text" placeholder="Enter your name" />
</div>
);
}
The above component App
is the main microfrontend we will expose in our project. You can similarly build more components and expose them using webpack.config.js
.
- Create a file
src/components/Main.js
and paste below code:
import * as React from 'react';
import App from './App';
import "./styles.css";
export default function MainApp() {
const [name, setName] = React.useState(null);
return (
<>
<h3 style={{ textAlign: 'center' }}>{ name ? <p>Your name is: {name}</p> : null }</h3>
<App onChange={(e) => setName(e.target.value)} />
</>
);
}
The MainApp
component is used for only one purpose i.e. to allow using the app like any other React app.
- Create a file
src/components/styles.css
and paste below code:
.MicroApp {
box-sizing: border-box;
margin: auto;
margin-top: 100px;
padding: 30px;
width: 95%;
border: 2px solid black;
border-radius: 12px;
font-family: sans-serif;
text-align: center;
}
.MicroApp input {
height: 32px;
border-radius: 8px;
border: 2px solid gray;
width: 350px;
padding-left: 10px;
padding-right: 10px;
}
2. Boot Up Your First MicroFrontend App
To start your microfrontend app server, just run below command:
npm start
The app server will start at port 8080
. Visit http://localhost:8080/ to open and check out your microfrontend app.
3. Integrating MicroFrontend in React
Integrating microfrontend app in other React is very simple. The microfrontend app will be hosted on the port 8080
and our second app will integrate it through it’s remote URL exposed via webpack.
- Create your project directory. Let’s call it:
micro-client
. - Run below command to initiate project and install all dependencies:
npm init -y
npm install react react-dom --save
npm install @babel/core @babel/preset-env @babel/preset-react babel-loader css-loader html-webpack-plugin sass sass-loader style-loader webpack webpack-cli webpack-dev-server --save-dev
- Open
package.json
file and copy paste below npm scripts:
"scripts": {
"build": "webpack",
"start": "webpack serve --watch-files ./src"
}
- Create a file
babel.config.json
and paste below config:
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
- Create a file
webpack.config.js
and paste below config:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const deps = require("./package.json").dependencies;
module.exports = {
mode: "development",
resolve: {
extensions: [".css", ".scss", ".js", ".jsx"],
},
module: {
rules: [
{
test: /\.s?css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: {
url: {
filter: (url) => {
if (url.startsWith("data:")) {
return false;
}
return true;
},
},
},
},
"sass-loader",
],
},
{
test: /\.jsx?$/,
use: ["babel-loader"],
exclude: /node_modules/,
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "public", "index.html"),
}),
new ModuleFederationPlugin({
name: "MICRO",
remotes: {
FIRST_APP: "FIRST_APP@http://localhost:8080/remoteEntry.js",
},
}),
],
};
In above config we are setting our microfrontend app remote FIRST_APP
.
- Create a directory
public
and create filepublic/index.html
and paste below code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Micro App</title>
</head>
<body style="width: 100%; height: 100%">
<div id="container"></div>
</body>
</html>
- Create a directory
src
and create filesrc/index.js
and paste below code:
import React from 'react';
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
const rootElement = document.getElementById("container");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<App />
</StrictMode>
);
- Create a directory
src/App.js
and paste below code:
import React, { lazy, Suspense } from "react";
import "./styles.css";
const FirstApp = lazy(() => import("FIRST_APP/app"));
const App = () => {
const [name, setName] = React.useState(null);
return (
<div className="App">
<h1>This is second app</h1>
<h2>Micro host app is integrated here</h2>
{ name ? <p>Your name is: {name}</p> : null }
<div>
<Suspense fallback={<span>Loading...</span>}>
<FirstApp onChange={(e) => setName(e.target.value)} />
</Suspense>
</div>
</div>
);
};
export default App;
We are using
Suspense
for loading our remote microfrontend app in this React app.
- Create a file
src/components/styles.css
and paste below code:
.App {
font-family: sans-serif;
text-align: center;
}
Run below command to start second app:
npm start
The app will start at port 8081
. Visit http://localhost:8081/ to open and check out the application.
The microfrontend app is loaded using it’s remote URL in our second React app.
The code for this tutorial can be found here.
Conclusion
In conclusion, building reusable, future-proof applications is essential for any business or organization that wants to stay ahead of the curve. Using microfrontends with React and Webpack Module Federation is an excellent way to achieve this goal.
By breaking down the front-end into smaller, independent components, you can create a more flexible and modular application. This makes it easier to maintain and update the application, and also reduces the amount of duplicate code you need to write.