Webpack 5 Module Federation : Exchange code between applications
Tags: programming, javascript, webdev, typescript
Published: 2023-02-27 12:56:26

Module Federation is one of the most exiting feature that was introduced in [Webpack 5](https://webpack.js.org/concepts/module-federation/) [release](https://webpack.js.org/blog/2020-10-10-webpack-5-release/#module-federation) dated 2020-10-10.

With this there has been a huge change on how we architect our modern application we build today.

## What is Module Federation(MF)?

Module Federation(MF) aims to solve the fiction sharing of modules in a distributed system. To simplify things we can bring in modules of our code across multiple applications. 

The code can contain :: state of the application, library dependencies that will be made available within a common registry managed by the Webpack container.

> Module Federation allows a JavaScript application to dynamically load code from another application and  in the process, share dependencies.
Zack Jackson (Creator of Webpack)

It’s important to note that this system is designed so that each completely standalone build/app can be in its own repository, deployed independently, and run as its own independent SPA.

** 💡 This is often known as Micro-Frontends, but is not limited to that.**

## Setting up Module Federation

We will take a look into how can we use MF in our demo React application. We will just start with create-react-app for simplicity.

**First React App**

```javascript
mkdir applicaion01
cd applicaion01
npm init -y
npm install react react-dom --save
npm install @babel/core @babel/preset-env @babel/preset-react babel-loader css-loader html-webpack-plugin webpack webpack-cli webpack-dev-server --save-dev
```

With the above commands in place we can make our first change inside of _**package.json**_ file

```javascript
"scripts": {
  "build": "webpack",
  "start": "webpack serve --watch-files ./src"
}
```

Make sure you are in your current project directory. We will need a change the babel presets as below

```javascript
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ]
}
```

Lets start with our **webpack.config.js** file and past the code below

```javascript
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const deps = require("./package.json").dependencies;

module.exports = {
  mode: "development",
  resolve: {
    extensions: [".css", ".js", ".jsx"],
  },
  module: {
    rules: [
      {
        test: /\.s?css$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              url: {
                filter: (url) => {
                  if (url.startsWith("data:")) {
                    return false;
                  }
                  return true;
                },
              },
            },
          }
        ],
      },
      {
        test: /\.jsx?$/,
        use: ["babel-loader"],
        exclude: /node_modules/,
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: "asset/resource",
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "public", "index.html"),
    }),
    new ModuleFederationPlugin({
      name: "APP_ONE",
      filename: "remoteEntry.js",
      exposes: {
        "./app": "./src/components/App",
      },
    }),
  ],
};
```

In the code above we set up couple of [loaders](https://webpack.js.org/loaders/) needed for our application. We named our (first) application **application01** because we will be sharing code across multiple application/projects.

Let's continue with editing our new file **app.js** file. 

```javascript
import React from 'react';
import "./styles.css";

export default function App({ onChange }) {
  return (
    <>
      <input onChange={onChange} type="text" placeholder="Enter your address" />
    </>
  );
}
```
 
The above component App is the main microfrontend we will be exposing in our project. 

We can add more components and expose them using _**webpack.config.js**_ file which we will dive into

Lets edit our main file **main.js** where we will be importing the file we created before **app.js** into this file.

```javascript
import {useState} from 'react';
import App from './app';
import "./styles.css";

export default function MainApp() {
  const [address, setAddress] = useState('');
  return (
    <>
      <h3>{ address ? <p>Your address {address}</p> : null }</h3>
      <App onChange={(e) => setAddress(e.target.value)} />
    </>
  );
}
```

With this in place we can see a input file where we could type our address inside and see the binding state changed.

##Creating second React app

Lets create another project and name it application02

**Second React App**

```javascript
mkdir applicaion02
cd applicaion02
npm init -y
npm install react react-dom --save
npm install @babel/core @babel/preset-env @babel/preset-react babel-loader css-loader html-webpack-plugin webpack webpack-cli webpack-dev-server --save-dev
```

We will also change our package.json file as we did before something like 

```javascript
"scripts": {
  "build": "webpack",
  "start": "webpack serve --watch-files ./src"
}
```

Also create a babel.config.json file alike config below

```javascript
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ]
}
```

Create our webpack.config.js file and paste the code below

```javascript
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const deps = require("./package.json").dependencies;

module.exports = {
  mode: "development",
  resolve: {
    extensions: [".css", ".js", ".jsx"],
  },
  module: {
    rules: [
      {
        test: /\.s?css$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              url: {
                filter: (url) => {
                  if (url.startsWith("data:")) {
                    return false;
                  }
                  return true;
                },
              },
            },
          }
        ],
      },
      {
        test: /\.jsx?$/,
        use: ["babel-loader"],
        exclude: /node_modules/,
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "public", "index.html"),
    }),
    new ModuleFederationPlugin({
      name: "applicaion02",
      remotes: {
        APP_ONE: "applicaion01@http://localhost:8080/remoteEntry.js",
      },
    }),
  ],
};
```

We have done the same process adding needful loaders to our application and setup our app remote as applicaion01

Let's go ahead to edit our app.js file inside of our src folder. Below are some changes 

```javascript
import { lazy, Suspense } from "react";
import "./styles.css";

const AppOne = lazy(() => import("APP_ONE/app"));

const App = () => {
  const [address, setAddress] = useState('');

  return (
    <>
      <h3>{ address ? <p>Your address {address}</p> : null }</h3>
      <div>
        <Suspense fallback={<span>Loading...</span>}>
          <AppOne onChange={(e) => 
 setAddress(e.target.value)} />
        </Suspense>
      </div>
    </>
  );
};

export default App;

```

We are making use of [react lazy loading](https://beta.reactjs.org/reference/react/lazy) to load parts of our application. We are also importing suspense so that we could wrap our remote React micro app we created. 

Where we have alike micro-frontend architecture going on.

We have successfully to this point imported modules from another app and used it inside of another application. We have implemented something basic, we just scratched the surface.

The awesome thing is this also works well with Server Side Rendering(SSR) means we can use this inside of any application/framework unless its using Webpack as a module bundler your code chunks.

##Conclusion

We used Webpack 5 Module Federation to consume and share micro-frontend components. This brings a huge gap of possibilities using Federated Modules.  

![Snowboarding](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3m8ee701cmbj9qrf2qky.gif)

##Reference readings

https://webpack.js.org/concepts/module-federation/

https://medium.com/swlh/webpack-5-module-federation-a-game-changer-to-javascript-architecture-bcdd30e02669

https://dev.to/marais/webpack-5-and-module-federation-4j1i

https://javascript.plainenglish.io/next-js-11-module-federation-and-ssr-a-whole-new-world-6da7641a25b4