Build Micro Frontends In React In 3 Easy Steps

Amit Kumar
Bits and Pieces
Published in
8 min readFeb 22, 2023

--

Photo by ANIRUDH on Unsplash

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.

See the full solution in its remote Bit scope

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 file public/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 file src/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 file App.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 file public/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 file src/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.

--

--

Amit Kumar is a frontend developer who love designing web applications with clean, consistent User Interface.