Module Federation Lifehack - Custom Container Name
Tags: modulefederation, microfrontends, webpack, webdev
Published: 2022-11-27 17:07:05

---
title: Module Federation Lifehack  - Custom Container Name
published: true
date: 2022-10-02 20:14:12 UTC
tags: modulefederation,microfrontends,webpack,webdev 
canonical_url: https://okhivrych.io/module-federation-lifehack-custom-container-name
---

### Intro

I have been using Module Federation Plugin for almost **two years** and have successfully used them for implementing **micro-frontend** architectures for many organisations. I decided to start sharing lifehacks related to module federation, which may save some time for other engineers trying to integrate Module Federation into their projects.

If you are not familiar with Module Federation Plugin, you can read my article at Beamery Hacking Talent Blog [Module Federation for distributed front ends the best of both worlds?](https://medium.com/hacking-talent/module-federation-for-distributed-front-ends-the-best-of-both-worlds-bb6af9e4000e)

### Problem statement

If you use monorepo for your micro-frontends, coupled with tools such as [Yarn Workspaces](https://classic.yarnpkg.com/lang/en/docs/workspaces/), [NX](https://nx.dev/) or [Turborepo](https://turborepo.org/), you probably use [package scope](https://docs.npmjs.com/cli/v8/using-npm/scope) for your monorepo. Scopes are a way of grouping related packages together. For example: `@somescope/somepackagename`

Let's say you have monorepo with two micro-frontend apps. The name of each micro app in `package.json` will be:

- `@my-micro-fe/app1`- host.
- `@my-micro-fe/app2`- remote container

And in the host `@my-micro-fe/app1`, you want to import the module with the Button component from remote container `@my-micro-fe/app2`, example:

```javascript
const RemoteButton = React.lazy(() => import('@my-micro-fe/app2/Button'));

```

Sounds good! However, to make it work, we need to configure Module Federation Plugin correctly.

Flowing [documentation](https://webpack.js.org/concepts/module-federation/), we should specify such a Module Federation config for `@my-micro-fe/app2`:

```javascript
new ModuleFederationPlugin({
  name: '@my-micro-fe/app2',
  filename: 'remoteEntry.js',
  exposes: {
     './Button': './src/Button',
   },
   shared: ['react', 'react-dom'],
 }),

```

If you then run webpack build command, you will receive an error:

```
Error: Library name base (@my-micro-fe/app2) must be a valid identifier when using a var 
declaring library type. Either use a valid identifier (e. g. _my_micro_fe_app2) or use a 
different library type (e. g. 'type: "global"', which assign a property on the global scope
 instead of declaring a variable). Common configuration options that specific library names
are 'output.library[.name]', 'entry.xyz.library[.name]', 'ModuleFederationPlugin.name' and 'ModuleFederationPlugin.library[.name]'.

```

### Solution

The problem is Module Federation plugin under the hood uses variable declaration for remote container scope. To fix this issue, you need to sanitize your app name to remove `/,-,@` special characters and add a few more lines in the configuration with `library.name` and `library.type` properties:

```javascript
new ModuleFederationPlugin({
   name: '@my-micro-fe/app2',
    filename: 'remoteEntry.js',
    exposes: {
      './Button': './src/Button',
    },
    library: {
      type: 'global',
      name: '_micro_fe_app2'
    },
    shared: ['react', 'react-dom'],
}),

```

The same fix you need to do in the host app:

```javascript
    new ModuleFederationPlugin({
      name: '@my-micro-fe/app1',
      library: {
        type: 'global',
        name: '_micro_fe_app1'
      },
      remotes: {
        "@my-micro-fe/app2": `promise new Promise(resolve => {
          const remoteUrlWithVersion = 'http://localhost:3002/remoteEntry.js'
          const script = document.createElement('script')
          script.src = remoteUrlWithVersion
          script.onload = () => {
            // the injected script has loaded and is available on window
            // we can now resolve this Promise
            const proxy = {
              get: (request) => window._micro_fe_app2.get(request),
              init: (arg) => {
                try {
                  return window._micro_fe_app2.init(arg)
                } catch(e) {
                  console.log('remote container already initialized')
                }
              }
            }
            resolve(proxy)
          }
          // inject this script with the src set to the versioned remoteEntry.js
          document.head.appendChild(script);
        })
        `
      },
      shared: ['react', 'react-dom'],
    }),

```

As you can see, for our `@my-micro-fe/app2` host app, I have also sanitized the name. Moreover, I am using [`pormise new Promise`](https://webpack.js.org/concepts/module-federation/#promise-based-dynamic-remotes) syntax to load `remoteEntry.js` file from the remote container `@my-micro-fe/app1`. That is why I also use the sensitized name to save the result of a request to the `window` object.

### In addition

This was just a simple example and you can instead create a base `webpack` config where you can sanitize the app name and generate correct remotes config using `pormise new Promise` to reduce code duplication.

#### I hope this tip was helpful for you!

Cheers! 🍺