Scalable and Maintainable Frontends: A Deep Dive into Microfrontends and Webpack Module Federation - Part 1

mblancodev

Manuel Blanco

Posted on October 26, 2023

Scalable and Maintainable Frontends: A Deep Dive into Microfrontends and Webpack Module Federation - Part 1

In this article we will explain the main concepts behind webpack module federation and then we will continue into implementing a series of exposable components (We will explain what that means later on), that will help us have a better frontend architecture.

Requisites:
  • Git, Github
  • Node, NPM
  • React, JavaScript ES6+
  • How to setup a minimal Webpack configuration
Considerations
  1. Webpack Module Federation: it is a plugin; It is not included when you first install Webpack itself.
  2. MF => Module Federation (not the other word haha - sweats in GenZ)
  3. In order for to get the most out of MF Plugin you need to have your dependencies in sync. Otherwise Webpack is gonna complain about it (You can work around it, but the configuration changes a bit)
  4. This is not a beginners friendly tutorial. Although, I'll explain everything some things may not be fully comprehensible unless you have prior experience in Front End Development.

Key Concepts

Configuration: You have to be specific about which modules of your application you want to share with other applications and either you want to consume from other applications or not. You'll have a webpack config for each different application.

Remote Entry Points: You've to create a remote entry point, this defines which modules you want to expose for remote use. These modules are packaged separately and made available as a remote entry point.

Consuming Remote Modules: The remote modules are fetched and executed at runtime. You can specify the remote entry points to be loaded asynchronously, and Webpack will take care of loading and managing them.

Runtime Integration: Webpack's runtime takes care of loading and integrating the remote modules. It handles various aspects like loading remote scripts, making remote modules available to the local application, and handling communication between the applications.

Versioning: Module Federation allows you to specify versions for shared modules. This helps ensure that the consuming application gets the correct version of a module and doesn't conflict with other versions used by different parts of your application.

Alright without anything more to add. Let's start coding!

Setting up the project

I have already created the starter projects we will be working with. You will see why we need two, in the following section.

You can find then here:

You will need to create your own repo since these are templates (name them: host_app and remote1 accordingly) and download the required dependencies, once you do that, come back, and we will start getting coding right away.

--

Hello world, w/ microfrontends

You know what is coming, yes! The mighty counter ;) ...

Let's create a button in remote1 that updates the state inside the host app. Nothing too fancy :)

Remote1 -- This one would be running in localhost:3000

-- Note: Is important to know where is running what. You'll see why later on :)

Let's start creating our Button.js in one of our folders inside remote1:

Button.js
export const Button = (props) =>
  <button type="button" onClick={props.onClick}> Click me! </button>
Enter fullscreen mode Exit fullscreen mode

Then we need to expose this component inside our webpack.config.js as follow:

webpack.config.js
 ...
  plugins: [
    ...other_plugins,
    new ModuleFederationPlugin({
      name: "app_name", // Change this
      filename: "remoteEntry.js",
      exposes: {
        // your custom components, functions, you wanna expose
        "./Button.js": "./src/Button.js",
      },
      shared: [
        deps,
        {
          react: {
            eager: true,
            singleton: true,
            requiredVersion: deps.react,
          },
        },
        {
          "react-dom": {
            eager: true,
            singleton: true,
            requiredVersion: deps["react-dom"],
          },
        },
      ],
    }),
  ],
 ...
Enter fullscreen mode Exit fullscreen mode

HOST_APP -- This would be running in localhost:8080

We switch over to our host_app, and inside the webpack.config.js, we read from remote1 at localhost:3000 the remoteEntry.js that exposes:

webpack.config.js
...
plugins: [
  ...other_plugins,
  new ModuleFederationPlugin({
      name: "host", // You can change this if you want :)
      remotes: {
        // Your known remotes
        "remote1": "app_name@http://localhost:3002/remoteEntry.js",
      },
      shared: [
        deps,
        {
          react: {
            eager: true,
            singleton: true,
            requiredVersion: deps.react,
          },
        },
        {
          "react-dom": {
            eager: true,
            singleton: true,
            requiredVersion: deps["react-dom"],
          },
        },
      ],
    }),
],
Enter fullscreen mode Exit fullscreen mode

--

Finally, inside our App.js in the host_app we import the component from the remote1 app. I know it gets kinda confusing using the terms this app this other app, but please bear with me. I'll leave a .dio example file that better explains it (in the resources panel is the link to the extension that allows you read .dio files directly from VSCode)

App.js
import Button from 'remote1/Button';

const App = () => {
  const [counter, setCounter] = React.useState(0);
  return (
    <div>
      <h5>Hello from HOST APP</h5>
      <p>HOST Counter: {counter}</p>
      <hr />
      <div class="border border-gray-200 rounded-md">
        <b>Container for remote components</b>
        <Button onClick={() => setCounter(counter+1)} />
      </div>
    </div>
  )
};

export default App;
Enter fullscreen mode Exit fullscreen mode

--

Once we do all of that we can run: npm run dev inside each project and everything should be running as expected.

PD: If you run into issues please don't hesitate to message me or leave a comment :) I'll help as soon as I can

Conclusions

As recap, we created one relatively small component that is being exposed from remote1 and read from what we called the host application.

That's all for now. Let me know in the comments if this was helpful :).

In PART II we will explore more how to extend an customize the webpack configuration of Module Federation.

Resources:

  1. https://webpack.js.org/plugins/module-federation-plugin/
  2. https://martinfowler.com/articles/micro-frontends.html
  3. https://www.aplyca.com/en/blog/micro-frontends-what-are-they-and-when-to-use-them
  4. https://marketplace.visualstudio.com/items?itemName=hediet.vscode-drawio
💖 💪 🙅 🚩
mblancodev
Manuel Blanco

Posted on October 26, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related