DEV Community

Cover image for Setup Micro Frontend Application with React Js
Shivam Pawar
Shivam Pawar

Posted on • Edited on

Setup Micro Frontend Application with React Js

In this article we are going to setup a micro frontend application with well known CLI tool "create-mf-app" which is a bootstrap tool to setup Module Federation.

What is Micro Frontend?🤨

Micro-frontend designs break down a front-end application into little, semi-independent "microapps" that work together loosely. This can aid in the management of complex projects.

Setting up project

Now we are going to setup a project which contains two micro frontends and one container app which will hold the all micro frontend apps.
In this article we are focusing on only setting up project in React js only but we can have multiple framework.

Open a cmd/terminal where you are going to setup project and enter below command.



npx create-mf-app


Enter fullscreen mode Exit fullscreen mode

If create-mf-app is already installed then it will directly prompt for application name and if not then it will ask to install it just simply enter 'y' and it will install it for you.

Once create-mf-app is installed, it will ask for the following info.

create-mf-app

I created a Shell app and given port 3000 to run the application.

Lets create another micro app with name 'common-components-lib'
We will follow same step as above but just different port and name.

common-components-lib

I created a Shell app and given port 3000 to run the application.

Now you need to move inside of each app and enter below command to build and run the app.
npm install && npm start

I recommend to open these 2 apps in two different cmd/terminal.

npm-install

I created a common-components-lib app and given port 3001 to run the application.

Our basic idea behind creating these 2 app is that Shell will be our container for all the micro apps and common-components-lib will contains common components like Header, Footer, etc.

We will now open Folder in a Code editor. Mine Favorite editor is Visual Studio Code❤ but you can use code editor of your choice.

After opening it, your folder structure will look like this.

folder-structure

Note: make sure both apps are up and running on different ports.

app-preview

app-preview-common

Creating Components inside micro app

Now lets add one Header and Footer Component inside the common-components-lib app.

Create a Header Component and paste below code.



import React from 'react';
import './header.css';

function Header() {
  return (
    <div class="header">
      <a href="#default" class="logo">
        Header Component
      </a>
      <div class="header-right">
        <a class="active" href="#home">
          Home
        </a>
        <a href="#contact">Contact</a>
        <a href="#about">About</a>
      </div>
    </div>
  );
}

export default Header;



Enter fullscreen mode Exit fullscreen mode

Also add one header.css file for styling the component.



.header {
    overflow: hidden;
    background-color: #f1f1f1;
    padding: 20px 10px;
}

.header a {
    float: left;
    color: black;
    text-align: center;
    padding: 12px;
    text-decoration: none;
    font-size: 18px;
    line-height: 25px;
    border-radius: 4px;
}

.header a.logo {
    font-size: 25px;
    font-weight: bold;
}

.header a:hover {
    background-color: #ddd;
    color: black;
}

.header a.active {
    background-color: dodgerblue;
    color: white;
}

.header-right {
    float: right;
}

@media screen and (max-width: 500px) {
    .header a {
        float: none;
        display: block;
        text-align: left;
    }

    .header-right {
        float: none;
    }
}


Enter fullscreen mode Exit fullscreen mode

Add Footer Component and header.css



import React from 'react';
import './footer.css';

function Footer() {
  return (
    <div class="footer">
      <p>Footer Component</p>
    </div>
  );
}

export default Footer;



Enter fullscreen mode Exit fullscreen mode

add footer.css



.footer {
    position: fixed;
    left: 0;
    bottom: 0;
    width: 100%;
    background-color: #f1f1f1;
    color: black;
    text-align: center;
}


Enter fullscreen mode Exit fullscreen mode

Import Header and Footer Component in App Component.



import React from 'react';
import ReactDOM from 'react-dom';
import Footer from './Footer';
import Header from './Header';

import './index.css';

const App = () => (
  <>
    <Header />
    <Footer />
  </>
);
ReactDOM.render(<App />, document.getElementById('app'));



Enter fullscreen mode Exit fullscreen mode

Our app will look like this after adding Header and Footer.

app-preview

Exposing Components to remotes

Now lets expose these two components so that we can use it in Shell app.

Open webpack.config.js of common-components-lib and update code inside plugin like this:



new ModuleFederationPlugin({
      name: 'common_components_lib',
      filename: 'remoteEntry.js',
      remotes: {},
      exposes: {
        './Header': './src/Header.jsx',
        './Footer': './src/Footer.jsx',
      },
      shared: {
        ...deps,
        react: {
          singleton: true,
          requiredVersion: deps.react,
        },
        'react-dom': {
          singleton: true,
          requiredVersion: deps['react-dom'],
        },
      },
    }),


Enter fullscreen mode Exit fullscreen mode

Note: We updated only "exposes".

Consume components in Shell app.

Now open webpack.config.js of Shell app and update code inside plugin.



const HtmlWebPackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

const deps = require('./package.json').dependencies;
module.exports = {
  output: {
    publicPath: 'http://localhost:3000/',
  },

  resolve: {
    extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'],
  },

  devServer: {
    port: 3000,
    historyApiFallback: true,
  },

  module: {
    rules: [
      {
        test: /\.m?js/,
        type: 'javascript/auto',
        resolve: {
          fullySpecified: false,
        },
      },
      {
        test: /\.(css|s[ac]ss)$/i,
        use: ['style-loader', 'css-loader', 'postcss-loader'],
      },
      {
        test: /\.(ts|tsx|js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
    ],
  },

  plugins: [
    new ModuleFederationPlugin({
      name: 'shell',
      filename: 'remoteEntry.js',
      remotes: {
        common_components_lib:
          'common_components_lib@http://localhost:3001/remoteEntry.js',
      },
      exposes: {},
      shared: {
        ...deps,
        react: {
          singleton: true,
          requiredVersion: deps.react,
        },
        'react-dom': {
          singleton: true,
          requiredVersion: deps['react-dom'],
        },
      },
    }),
    new HtmlWebPackPlugin({
      template: './src/index.html',
    }),
  ],
};



Enter fullscreen mode Exit fullscreen mode

Note: We updated only remote.

Make sure you use same name as the app name inside the plugin.
In our case app name is "common_components_lib" and "shell"

webpackConfig

webpackConfigShell

Render remote(Components from micro apps) in Shell App

Now its time to test our application by actually importing remote components in Shell application.

Inside App.jsx, import our Header and Footer components from common-components-lib app.



import React, { Suspense } from 'react';
import ReactDOM from 'react-dom';

const Header = React.lazy(() => import('common_components_lib/Header'));
const Footer = React.lazy(() => import('common_components_lib/Footer'));

import './index.css';

const App = () => (
  <>
    <Suspense fallback={<>Loading...</>}>
      <Header />
      <Footer />
    </Suspense>
  </>
);
ReactDOM.render(<App />, document.getElementById('app'));



Enter fullscreen mode Exit fullscreen mode

Note: We used Lazy Loading to import the remote components. To know more about the lazy loading read the official documentation from here.

Time to run the Shell App.

Restart both the application, as we made changes in webpack.config.js.

Once both apps restarted check if Header and Footer is visible on Shell app.

Yeah!!😍

final

Conclusion

This article is focused on the setup of the micro frontend application and we successfully configured it.

Please do share your feedback and experiences with Micro Frontend in the comments section below. I’d love to see what you come up with!

If you found this article useful, please share it with your friends and colleagues!❤️

Read more articles on Dev.To ➡️ Shivam Pawar

Follow me on ⤵️
🌐 LinkedIn
🌐 Github

Top comments (1)

Collapse
 
spenceg85 profile image
Info Comment hidden by post author - thread only accessible via permalink

Isn't it bad practice to create an MFE shell with a framework at all? For example: if you create a React MFE with react-router-dom v6 and integrate it into a shell app that was created with react-router-dom v5 as a shared library and contains all v5 syntax, the MFE will be incompatible with the shell itself, as Module Federation will always choose v5 from the shell as a shared dependency and throw errors due to v5 lacking the new RRD hooks syntax. Also, a React shell would make it much harder to integrate MFE's that were created in another framework... Would it make more sense to limit the shell itself to strictly a vanilla JS? Creating Micro Frontends with these types of dependencies creates an architecture with all of the drawbacks of microservices and none of the benefits....

Some comments have been hidden by the post's author - find out more