DEV Community

Pahan Perera
Pahan Perera

Posted on

React Micro Frontends with Webpack's Module Federation

Micro Frontends is a very popular topic in today's frontend world. Most of the teams tend to adopt this micro frontend strategy to develop their large and complex web applications, due to many advantages it provides such as,

  • Separate, decoupled codebases
  • Independent deployment
  • Incremental updates

Below diagram showcases some of the important concepts of micro frontends.

Micro Frontend Concepts

Most popular way of developing micro frontends is using HTML5 Web Components (Custom Elements). Angular like web frameworks has extended to support Web Components, while most of other libraries like React, supports it out of the box.

For more information about micro frontends you can refer to

What is Module Federation in Webpack ?

Webpack version 5 comes with a new feature called Module Federation, which helps to share code and dependencies between projects at runtime.

In high level, an application exposes certain component/s via a separate javascript file, and other application that wishes to use that component, async loads that remote javascript file and consume that component.

Webpack's Module Federation

Recent times this feature has changed the way we develop micro frontends.

In this post, I will walk through how you can develop React based micro frontends using Webpack's Module Federation by creating a simple bank application that shows a list of accounts and account details on a selected account.

This is how our final application architecture would look like.

Simple Bank Application using React

Let's get started..

All of the code in this post can be found here for your references.

Prerequisites

First of all, since this is about React and webpack, you should have a React application configured with Webpack.

Refer to this project if you need help doing that.

Also, as mentioned above, we will need three React projects for our application

  • accounts-summary-app - Micro frontend that provides the summary of all the accounts
  • account-details-app - Micro frontend that provides details of a selected account
  • main-app - app that hosts above two components. Also acts as a medium to communicate with each other.

Configure ModuleFederationPlugin in Micro Frontend Projects

ModuleFederationPlugin is a high level webpack plugin that provides a very convenient way to configure module federation in your projects. Also plugin comes along with webpack library without need of installing another dependency as well.

Responsibility of our micro frontend projects is to expose a component. So let's add following to webpack.config.js of accounts-summary-app project.

import the plugin

const { ModuleFederationPlugin } = require("webpack").container;
Enter fullscreen mode Exit fullscreen mode

configure the plugin

  plugins: [
    new ModuleFederationPlugin({
      name: "AccountsSummaryApp",
      filename: "accountsSummaryApp_remote.js",
      exposes: {
        "./AccountsSummary": "./src/components/AccountsSummary",
      },
    }),
    ...
  ],
Enter fullscreen mode Exit fullscreen mode
  • name is unique identification of your module. Normally this is the name of your micro frontend project.
  • filename is the name of the javascript file that exposes the components
  • exposes is a map (key and a value) of components that are exposed from this module. (key will act as an alias for the component while the value is where the component located in the project)

Now, let's run this project locally and see what happens.

Separate javascript file for exposed module

As you can see, now webpack has bundled our AccountsSummary component into a separate javascript file, as we instructed in the webpack configuration.

Let's do the same to the account-details-app project as well

  plugins: [
    new ModuleFederationPlugin({
      name: "AccountDetailsApp",
      filename: "accountDetailsApp_remote.js",
      exposes: {
        "./AccountDetails": "./src/components/AccountDetails",
      },
    }),
    ...
  ],
Enter fullscreen mode Exit fullscreen mode

In case you missed some thing, you can always refer to these two projects

accounts-summary-app
account-details-app

Configure ModuleFederationPlugin in Host App project.

Like I was explaining before, our host app, main-app is responsible for loading the components from micro frontend projects.

Like micro frontends configurations defines exposes, Host app's webpack configuration defines remotes that tells webpack where to find those external components.

  plugins: [
    new ModuleFederationPlugin({
      remotes: {
        AccountsSummaryApp_Remote: "AccountsSummaryApp@http://localhost:9001/accountsSummaryApp_remote.js",
        AccountDetailsApp_Remote: "AccountDetailsApp@http://localhost:9002/accountDetailsApp_remote.js",
      },
    }),
    ...
  ],
Enter fullscreen mode Exit fullscreen mode

remotes is a map (key and value) that defines all the external modules that it consumes. Key will act as an alias for the module and value defines the remote javascript file location for that module.

value should have a special format like below

<Name of the Exposed Module>@<Remote URL of the javascript file>

Now that all the webpack configurations are completed, let's write some javascript code to load external components.

Load External Components to Host App

One of the beautify thing about this webpack module federation is that, developers can not feel a difference between importing a local component from its own project and remote component from an external javascript file.

React code will look like you are lazy loading a component.

const AccountsSummary = React.lazy(() =>
  import("AccountsSummaryApp_Remote/AccountsSummary")
);
Enter fullscreen mode Exit fullscreen mode

and use that in your jsx

<Suspense fallback={<h1>Error while loading Account Summary</h1>}>
  <AccountsSummary onAccountSelected={handleAccountSelected} />
</Suspense>
Enter fullscreen mode Exit fullscreen mode

One thing to note about the import is that to use same alias that you define in the host application along with the component alias that you defined in your micro frontend project

Module Federation configurations

Communication between components

As mentioned earlier, external components are same as local components in your project. So standard ways of communication should be applicable here as well.

For this application, I have defined a shared state with in the host application and each component communicate via that shared state.

Refer to main-app to see the code.

Conclusion

This is very beginner level tutorial about how to develop Micro Frontends using Webpack's Module Federation feature. During this post I was able to briefly explain about

  • What are Micro Frontends
  • What is Webpack's Module Federation Feature
  • Configure Micro frontends to expose components
  • Configure Host app to use those exposed components
  • How to communicate between components

Full working example can be found here.

You can find the full React Template used in this sample bank application here.

That is all for now. Please share you feedbacks. Thank you for reading.

Top comments (2)

Collapse
 
aiwin_raj profile image
Aiwin Raj

Thank you so much for helping with the naming and imports. I was really confused with what to use in exposes {} remotes {} and for importing in the main. Your visual image helped me a lot.

Collapse
 
cesarpachon profile image
cesar pachon

hello I am a bit lost at the general concept, in particular: if I have a shell, and different UI's, let's say A and B (microfrontends), it is possible that the shell packs the React, react-dom, materialui dependencies and the A, B packages don't?
In my experiments, I see that the bundle size of shell, A and B are the same, so I suspect react, react-dom, etc are being bundled in ALL the apps, beating the goal of reducing bundle size by sharing remote dependencies. thanks for clarifying this!