How to configure Webpack Module Federation

A dive into Webpack Module Federation configuration

Microfrontends (MFEs) are here to stay and are something a Front End developer must know, nowadays. Although they can be implemented in different ways, Webpack Module Federation (WMF) is imposing as the industry standard. In this bite, I am going to dive into its configuration, hoping to save you some time building your first MFE.

What does it look like?

WMF configuration starts from the Weback configuration file, as usual for everything that is Webpack related. We should add the ModuleFederationPlugin to our configuration.

// webpack.config.js
module.exports = {
  // ... other config settings ...
  plugins: [
    new ModuleFederationPlugin({
      // this is what we're going to see in more detail
    })
  ]
}

What is passed to the plugin constructor changes according to what part of the MFE architecture we're building, either a host application or an MFE. Oftentimes, we may have MFEs that consume other MFEs, so they would need to be configured in both ways, in a hybrid manner. But before going through the most common cases, let's build up some concepts.

The concepts behind

A WMF MFE configuration is built upon the following concepts:

  • MFE: a self contained application deployed as a single javascript file produced by a build run with WMF configured.

  • Host: an application that downloads MFEs. Can be a MFE itself.

  • Main application: the end user application that will be the root Host.

  • Container: the runtime reference to a MFE used by the Host.

  • Exposure points: one or more entrypoints exposed by a Container that can be referenced by the Host.

  • Dependencies: the libraries/packages a MFE or Host requires for it to work properly. They can be embedded in the MFE or shared with the Host. If the latter, then it's up to the Host (or a Host upper in the chain) to provide them.

All of those entities work together and are orchestrated by this simplified flow:

  • users will access our web application which is the Main application, serving as root Host.

  • when loading, Main application will set up all the common stuff and download the MFEs required to be eagerly loaded.

  • as long as users will be using our Main application, more MFEs can be lazily loaded.

  • for each new MFE that is also a Host, other MFEs can be downloaded as well.

  • once a MFE is downloaded, it is loaded in memory using its container name and made available to imports statements

This high level picture should have given you a glimpse of what is going on without delving into the nitty gritty. If you wish more details, there's no better place than the WMF docs.

How to configure WMF

Let's go straight to the config and see how a minimal Host/MFE pair can be set up.

// MFE webpack.config.js
module.exports = {
  // ... other config settings ...
  plugins: [
    new ModuleFederationPlugin({
      name: "mfeContainer",
      exposes: { "./entrypoint": "./path/to/myEntryPoint.js" },
      filename: "mfe.js",
      shared: { react: { requiredVersion: "^18.2.0" } }
    })
  ]
}

// HOST webpack.config.js
module.exports = {
  // ... other config settings ...
  plugins: [
    new ModuleFederationPlugin({
      name: "host",
      remotes: {
        remoteMFE: `mfeContainer@http://localhost:3000/mfe.js`,
      },
      shared: { react: { requiredVersion: "18.2.0" } },
    }),
  ]
}

Let's explain those configurations.

In MFE configuration:

  • name is the container name, that will be used to reference the MFE from Host app. It is a required value.

  • exposes maps an exposure name to an entrypoint file. The exposure name is what will be used by the Host to import the MFE

  • filename is the one that will be used to emit the MFE file during the Webpack build

  • shared object contains the list of required modules the MFE expects to be loaded already

In Host configuration:

  • name the container name. It is a required value.

  • remotes is the core part of a Host. It maps an internal name to an MFE URL so that it can be downloaded when referred by the Host. Such MFE URL is made from MFE container name and the actual URL pointing to the MFE filename, separated by an @ char.

  • shared object tells Webpack what dependencies must be loaded beforehand

In hybrid configurations, when the MFE is also the Host, you need to configure it as an MFE but adding the remotes configuration.

How the code looks like

On MFE side, we just need to configure WMF properly to emit the javascript file that will be downloaded by Host app. The only thing to keep in mind is how to export from our myEntryPoint.js, but it is plain standard ES6 stuff

/// file myEntryPoint.js

// default export
const myEntryPointObj = { ... }
export default myEntryPointObj;

// named export
export const myEntryPointObj = { ... }

The Host app is a bit more interesting.

// if default export was used in MFE
import mfeEntrypointObj from "remoteMFE/entrypoint"

// if named export was used in MFE
import { mfeEntrypointObj } from "remoteMFE/entrypoint"
  • remoteMFE is the internal name given by Host WMF configuration to downloaded MFE.

  • entrypoint is the exposure name of the MFE entrypoint file

Conclusions

That's all folks! There is nothing fancy about Webpack Module Federation once you grasped the concepts behind. Unfortunately, I found the documentation a bit omitting on the core concepts and I had to figure out on my own. Or maybe it was just me.

Anyway, I hope you will find this simple how-to useful!

To the next bite!

References