Quick Summary:

This blog covers a complete guide on how one can implement React Micro frontend along with a firm understanding of the React Microservices Architecture, the benefits of such monolithic applications built using React Micro frontend, the various implementations of the same, and an ideal tutorial for building Micro frontend with React.

Table of Contents

Introduction to React Micro Frontend

Monolithic architecture, which has been the norm in software development for decades now, is being replaced by microservices as an alternate strategy. Initially, the success of monolithic designs has a long history. Consequently, several software vendors and industry leaders firmly believe in its benefits. But as times change, new technical advancements appear that are more advantageous than what everyone seems to be accustomed to.

React micro frontend architecture is not a new concept; rather, it is a progression of earlier architectural patterns. Disruptive innovation trends in social media, cloud computing, and the Internet of Things are heavily influencing the platform of microservice architecture to quickly penetrate the market.

Thanks to the move to continuous deployment. Microservices, in the form of React micro frontend help businesses in the following ways:

  • Scalability of Build-Up
  • Quick Deployment
  • Technology-independence
  • No Insulation fault
  • Efficient upgrade and migration
  • High deployability and automatability
  • Reduced security threats and dependability
  • Shortened and cheaper development time
  • Appealing to engineers

Building Micro Frontend with React [Tutorial]

Let us show you a tutorial guide to building React Micro frontend. Here, we will be building two applications, namely, a host and remote; where the main application is the host, and another one will be a sub-app that we plug into it.

The host app will be our “main” app and the remote app will be a sub-app plugging into it.

First, we will be creating a basic react app using create-react-app.

1. In Your Root Directory

Copy Text
npx create-react-app host-app
 
npx create-react-app remote-app

This will create two apps for you:

1. host-app/
2. remote-app/

2. Adding Dependencies

Within each host-app/ and remote-app/ run:

npm install –save-dev webpack webpack-cli html-webpack-plugin webpack-dev-server babel-loader css-loader

This will install webpack & dependencies that we need for webpack configuration.

NOTE:-Webpack Module Federation is available in version 5 and above of webpack.

After adding dependencies we can host our app.

Do you wish to create a React Micro Frontend application for your users to hastily find them their solution and to scale your business?
Hire Reactjs developers from Bacancy as we are early adopters of the magnificent possibilities in the frontend. Our veteran React developers have years-long experience with industry-wide domains of small & big dimensions.

3. Hosting the App

Let us start with our webpack configuration

Create a new webpack.config.js file at the root of host-app/ & remote-app/:

Copy Text
//host-app/webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
 
module.exports = {
 entry: "./src/index",
 mode: "development",
 devServer: {
   port: 3000,
 },
 module: {
   rules: [
     {
       test: /\.(js|jsx)?$/,
       exclude: /node_modules/,
       use: [
         {
           loader: "babel-loader",
           options: {
             presets: ["@babel/preset-env", "@babel/preset-react"],
           },
         },
       ],
     },
     {
       test: /\.css$/i,
       use: ["style-loader", "css-loader"],
     },
   ],
 },
 plugins: [
   new HtmlWebpackPlugin({
     template: "./public/index.html",
     favicon: "./public/favicon.ico",
     manifest: "./public/manifest.json",
   }),
 ],
 resolve: {
   extensions: [".js", ".jsx"],
 },
 target: "web",
};
Copy Text
// remote-app/webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
 
module.exports = {
 entry: "./src/index",
 mode: "development",
 devServer: {
   static: {
     directory: path.join(__dirname, "public"),
   },
   port: 4000,
 },
 module: {
   rules: [
     {
       test: /\.(js|jsx)?$/,
       exclude: /node_modules/,
       use: [
         {
           loader: "babel-loader",
           options: {
             presets: ["@babel/preset-env", "@babel/preset-react"],
           },
         },
       ],
     },
     {
       test: /\.css$/i,
       use: ["style-loader", "css-loader"],
     },
     {
       test: /\.(gif|png|jpe?g|svg)$/,
       use: [
         {
           loader: "file-loader",
           options: {
             name: "[name].[ext]",
             outputPath: "assets/images/",
           },
         },
       ],
     },
   ],
 },
 plugins: [
   new HtmlWebpackPlugin({
     template: "./public/index.html",
     favicon: "./public/favicon.ico",
     manifest: "./public/manifest.json",
   }),
 ],
 resolve: {
   extensions: [".js", ".jsx"],
 },
 target: "web",
};

This basic webpack example is to get our js and jsx code transpiled using babel-loader and injected into an HTML template.

Change the start script in package.json to utilize our webpack config:-

Copy Text
 "scripts":{
    "start": "webpack serve"
  }
index.js(same for both apps)

First, we need the index.js as an entry to our app. Another file we are importing is bootstrap.js which renders the React app.

We need this extra layer of indirection because it will give Webpack a chance to load all of the imports it needs to render the remote app.

Otherwise, you will see an error like this:

The shared module is not available for eager consumption

Copy Text
//host-app/src/index.js
//remote-app/src/index.js
import("./bootstrap");

// Note: It is important to import bootstrap file dynamically using import() statement otherwise you will see the same error.

bootstrap.js(same for both apps)

Next, we define the bootstrap.js file for both repos that renders our React application.

Copy Text
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
 
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
 < React. Strict Mode >
   < App / >
 </React.StrictMode>
);
App.js(In host App)

Now we can write our App.js file where the app’s main logic happens. Here we will load two components from remote which we will define later.

import(“Remote/App”) will dynamically fetch the Remote app’s App.js React component.

We need to use a lazy loader and also an ErrorBoundary component to create a smooth experience for users in case the fetching takes a long time or introduces errors in our host app.

Copy Text
// host-app/src/App.js
import React from "react";
import ErrorBoundary from "./ErrorBoundary";
const RemoteApp = React.lazy(() => import("Remote/App"));
const RemoteButton = React.lazy(() => import("Remote/Button"));
 
const RemoteWrapper = ({ children }) => (
 <div
   style={{
     border: "1px solid red",
     background: "white",
   }}
 >
   <ErrorBoundary>{children}</ErrorBoundary>
 </div>
);
 
export const App = () => (
 <div style={{ background: "rgba(43, 192, 219, 0.3)" }}>
   <h1>This is the Host!</h1>
   <h2>Remote App:</h2>
   <RemoteWrapper>
     <RemoteApp />
   </RemoteWrapper>
   <h2>Remote Button:</h2>
   <RemoteWrapper>
     <RemoteButton />
   </RemoteWrapper>
   <br />
   <a href="http://localhost:4000">Link to Remote App</a>
 </div>
);
export default App;

You can add the ErrorBoundary component from this repo.

You Might Also Like To Read:

Top React Static Site Generators for 2022
Add Module Federation(In host App)

We’re not ready to run the app just yet. Next, we need to add Module Federation to tell our host where to get the Remote/App and Remote/Button components.

In our webpack.config.js we introduce the ModuleFederationPlugin:

Add the following code to the plugins.

Copy Text
// host-app/webpack.config.js
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const { dependencies } = require("./package.json");
 
module.exports = {
 //...
 plugins: [
   new ModuleFederationPlugin({
     name: "Host",
     remotes: {
       Remote: `Remote@http://localhost:4000/moduleEntry.js`,
     },
     shared: {
       ...dependencies,
       react: {
         singleton: true,
         requiredVersion: dependencies["react"],
       },
       "react-dom": {
         singleton: true,
         requiredVersion: dependencies["react-dom"],
       },
     },
   }),
 ],
};

Important things to note:

Name:- It is used to distinguish the modules.

Remotes:- It is where we define the federated modules we want to consume in this app. You’ll notice we specify Remote as the internal name so that we can load the components using import(“Remote/“). But we also define the location where the remote’s module definition is hosted: Remote@http://localhost:4000/moduleEntry.js. This URL tells us three important things. The module’s name is Remote, it is hosted on localhost:4000, and its module definition is moduleEntry.js.

Shared:- It is how we share dependencies between modules. This is very important for React because it has a global state, meaning you should only ever run one instance of React and ReactDOM in any given app. To achieve this in our architecture, we are telling webpack to treat React and ReactDOM as singletons, so the first version loaded from any modules will be used for the entire app. As long as it satisfies the requiredVersion we define. We are also importing all of our other dependencies from package.json and including them here, so we can minimize the number of duplicate dependencies between our modules.

Now, if we run npm start in the host app we will be able to see the output on the screen.

This means our host app is configured, but our remote app is not exposing anything yet. So we need to configure that also.

For Remote App

Let’s start with the webpack configuration file. As we have configured it host-app, we have some knowledge of Module Federation:

Add the following code to the plugins.

Copy Text
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const { dependencies } = require("./package.json");
 
new ModuleFederationPlugin({
   name: "Remote",
 
   filename: "moduleEntry.js",
   exposes: {
     "./App": "./src/App",
     "./Button": "./src/Button",
   },
   shared: {
     ...dependencies,
     react: {
       singleton: true,
       requiredVersion: dependencies["react"],
     },
     "react-dom": {
       singleton: true,
       requiredVersion: dependencies["react-dom"],
     },
   },
 }),

The important things we can note from the above code are:

  • Our webpack dev server runs at localhost:4000
  • The remote module’s name is Remote
  • The filename is moduleEntry.js
  • Combining these will allow our host-app to find the remote-app’s code at Remote@http://localhost:4000/moduleEntry.js

exposes is where we define the code we want to share in the moduleEntry.js file. Here we are exposing two files: < App / > and < Button / >.

Now We can set up those components and our Remote app so it can run independently.

Copy Text
import React from "react";
 
export const App = () => {
 return <div>Hello from the Remote App</div>;
};
export default App;

And we also want to expose a < Button / > component.

Copy Text
import React from "react";
 
export const Button = () => <button>Hello!</button>;
 
export default Button;

Now We have configured the Remote app and if you run npm start we can see a blank page with “Hello from the other side.”

Now you can put both repos in one folder and start both on single command:-

Copy Text
Microfrontend-demo/host-app
Microfrontend-demo/remote-app

Run npm init -y

One package.json file will be created. Now add the following changes in package.json:-

Copy Text
  "workspaces": {
    "packages": [
      "host",
      "remote"
    ]
  },
  "scripts": {
"start": "npm run start:host & npm run start:remote",
    "start:host": "cd ./host-app && npm start",
    "start:remote": "cd ./remote-app && npm start",
    "start:all": "yarn workspaces run start",
    "cleanup": "yarn workspaces run cleanup"
  },

This will run both repos in a single command. And you can see the output.

Conclusion

The developer doesn’t recommend creating a react micro frontend App using the create-react-app command. However, we hope you found this tutorial helpful.

Typically, some crucial elements have contributed significantly to the popularity and momentum of micro frontend architecture. The bulk of product-based businesses uses the micro frontend architecture to speed up and lower the cost of development. In the upcoming years, monolith design might only be used for developing prototypes of new goods, while micro frontend architectures that offer modularity and simple scaling of programs might be employed for mainstream development.

Frequently Asked Questions (FAQs)

The core idea behind developing micro frontends is to be technology agnostic, to isolate team code, to establish team prefixes, to favor native browser features over custom APIs, and to build a resilient site.

Some of the evident cons of micro front-end architecture are a lack of control over interfaces between modules, increased payloads, complex development processes, and cohesive UX.

New feature roll outs, performance enhancements, and reusability of code are the pros of having a micro front-end architecture.

Hire React Developer to Build Your Next React Micro Front End

Get the following pros of micro frontends:

  • Incremental Upgrades
  • Simple, decoupled Codebase
  • Independent Deployment
  • Autonomous Teams

Our smart consultants will ease your processing.

Book a 30 min call

Build Your Agile Team

Hire Skilled Developer From Us

[email protected]

Your Success Is Guaranteed !

We accelerate the release of digital product and guaranteed their success

We Use Slack, Jira & GitHub for Accurate Deployment and Effective Communication.

How Can We Help You?