Micro Frontends: Benefits of Webpack 5 Module Federation – A comparison

In this article I am going to have a look at the benefits of module federation in micro frontends with Webpack 5. We will be creating two similar micro frontend websites using two different approaches and compare the amount of resources that will be transferred over the network.

Short overview of micro frontend approach:

The micro frontend concept aims to separate modern websites (main page) into smaller components that are individually hosted as webpages (component page) themselves. A component is dependent on the context and could a whole feature, for instance a shopping cart, or just an element that will be used on the main page like a button.

This approach is similar to micro services approach and the usage strongly dependent on context and development capabilities, while an ideal implementation offers several benefits as it offers the possibility to use various technologies within a single website or have independent teams working on autonomous parts of a complex site. Of course there are also downsides to this approach, one of them being possibly duplicated resources and large overheads when it comes to integrating component pages. Module Federation promises the tackle the issue of duplicated dependencies for projects that are built with Webpack 5.

To elaborate I am going to build a site using custom html elements approach and then use module federation approach to create the same exact site and compare them.

The code I will be referring to is located at:

Module Federation Approach: https://gitlab.mi.hdm-stuttgart.de/du009/modulefederationfrontends

HTML 5 Custom Elements Approach: https://gitlab.mi.hdm-stuttgart.de/du009/microends.git

Since my primary focus is to inspect the resource size between those two approaches and introduce the concepts in general, I might not go into great detail about the implementation. Instead I will try to explain the crucial steps briefly.

Example page we want to create:

Example micro frontend webstore

As you can see, we have 2 different websites included into 1 containing website:

Container (main page) – Host

ProductDetails (component page) – Component

ExploreProducts (component page) – Component

Approach without module federation: A react micro frontend using HTML Custom Elements to share components between host and consumer applications.

HTML Custom Elements approach is one way to implement micro frontends. The Component (component page) we want to share with our host application (main page) is going to be rendered through a custom defined HTML tag.

Our first step is to create the ProductDetails component itself, which is going to be simple React component.

productDetail.js

In the next step, we need to create a shell that provides the HTML Element. We create a custom javascript class that extends from HTML Element and therefore inherits certain methods. One of those methods that we need to overwrite is connectedCallback(). This method is called once the custom tag occurs in our main application and should include the initialisation logic of our component. In a more complex case, our component could fetch data or even include further components. In this example we are simply going to return our previously created React component.

App.js of ProductDetail

An important thing to note: in this approach, stylesheets are not automatically shared between the component and our main page. We have to dynamically insert a html style element into the DOM that contains the styling we want on our component (line 7 includes the css as string, line 53 inserts css into our created style tag).

To have an idea of what our component looks like, we are going to serve it locally. Webpack bundles our component (or application) and provides a development server. Bundling and server configurations are specified in our webpack configuration webpack.config.json

After running npm start our component is served at http://localhost:3002 and looks like this:

ProductDetail at localhost:3002

We now repeat the same procedure for our second component ExploreProducts, which looks like this.

ExploreProducts at localhost:3001

Once we set up our components, we have to build our host application / container (main page) and enable it to consume the components we just built. Its going to be a simple React page that includes some logic to fetch our components (loadMicrofrontendAsComponent()) and then inserts the custom HTML (e.g. <product-detail></product-detail>) tags where we want them to be (line 34 and line 37).

App.js from container application

Once we have all components running locally and start our host application the result will look like this:

Micro frontend at localhost:3000

Upon inspection of the network tab, we can identify our two components being loaded over the network with a respective size of about 1.6 MB. This is quite large, as it includes two completely bundled applications. All dependencies of our components, like React or React-DOM are included in this file. This leads to a problem with micro frontends using this approach: We use the same version of React in both our components, yet we still include the library twice in our host application, producing an unnecessary overhead.

Network inspection of host application at localhost:3000

Module Federation approach with Webpack 5: A react micro frontend using Webpack 5 bundling to share components between applications

In contrast to our first example, we are now going to create the same exact website using Webpack 5 Module Federation.

We will be starting off creating the two components we want to use in our host application (main page): ExploreProducts and ProductDetail.

They are the same simple React components and look as follows:

productDetail.js from module federation approach
exploreProducts.js from module federation approach

which produce this result when served locally via Webpack:

ProductDetail Component at localhost:3003
ExploreProducts Component at localhost:3002

In order to enable our components to be consumed by a host application, we have to include the Module Federation Plugin into our Webpack 5 configuration and specify the name of our component (line 40) as well as tell the plugin where it can find the component we want to provide (line 44).

webpack.config.json for product detail component
webpack.config.json for explore products component

Now we have to build our host application and specify the remote components we want to import in its webpack configuration.

App.js of container application
webpack.config.json from app container

Once we served all our components and the host application via npm start we can now see our micro frontend running at localhost:3001.

micro frontend at localhost:3001

If we open the network inspector we can identify the requests that load our remotely hosted components. The remote entry files for each component (remoteEntry.js) as well as the components themselves (src_exploreProducts_js.js and src_productDetail_js.js) are requested over the network. Additionally some requests to handle sharing and versioning of the dependencies specified as shared in the webpack configs of the remotes. If we compare the sizes of the response data to the size of response data used in the custom elements approach, we can immediately see that even tough there are more requests in module federation, the total size is much smaller. This will most likely result in generally faster loading times for micro frontends built with this approach.

Conclusion

Webpack 5 Module Federation is a really useful, yet still somewhat complex approach to implement micro frontends. It offers huge potential when creating distributed applications and managing custom components or dependencies that are supposed to be shared across different parts of the application.

In general there are many ways to implement micro frontends and share components between applications and part of the development process should be to specify the needs and dependencies that are going to occur in the project and choose implementations based on the results.

In this article I showcased a really narrow and not complex example of two different approaches when it comes to implementing a micro frontend structure. Further resources and details about techniques used in this example can be found at:

https://module-federation.github.io/

https://github.com/module-federation/module-federation-examples

https://medium.com/@gilfink/wrapping-react-components-inside-custom-elements-97431d1155bd”>https://medium.com/@gilfink/wrapping-react-components-inside-custom-elements-97431d1155bd