Mastering Code Sharing in Micro‑Frontends with Webpack 5 Module Federation

This article explores various strategies for sharing code across micro‑frontend applications—including NPM packages, monorepos, Webpack DLLPlugin, Externals, and the powerful Webpack 5 Module Federation—detailing their configurations, performance trade‑offs, and practical implementation steps to achieve efficient, dynamic module sharing.

ELab Team
ELab Team
ELab Team
Mastering Code Sharing in Micro‑Frontends with Webpack 5 Module Federation

Preface

In increasingly complex front‑end scenarios, splitting a large application into independent micro‑apps improves build efficiency, but sharing common code among them remains a challenge.

Common Solutions

NPM Dependencies

Publishing shared modules as npm packages allows each micro‑app to depend on them, yet code is still bundled repeatedly, version upgrades are costly, and the same library may be loaded multiple times.

Monorepo

Placing shared code as a sub‑project in a monorepo and importing it as a dependency also results in duplicate bundling during build.

{
  "name": "@package/a",
  "dependencies": {
    "@package/common": "0.0.1"
  }
}

Although version issues are avoided, the build still includes duplicate code.

Webpack DLLPlugin

The DLLPlugin lets you pre‑bundle common modules into a dynamic link library, so builds only need to compile business code unless the shared modules change.

Package shared dependencies and generate a manifest.json describing the DLL.

module.exports = {
  resolve: {
    extensions: [".js", ".jsx"]
  },
  entry: {
    alpha: ["./a", "./b"]
  },
  output: {
    path: path.join(__dirname, "dist"),
    filename: "MyDll.[name].js",
    library: "[name]_[fullhash]"
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.join(__dirname, "dist", "[name]-manifest.json"),
      name: "[name]_[fullhash]"
    })
  ]
};

Use the manifest to map module names to DLL IDs at runtime.

{"name":"alpha_32ae439e7568b31a353c","content":{"./a.js":{"id":1,"buildMeta":{}},"./b.js":{"id":2,"buildMeta":{}}}}

Reference the DLL in the HTML entry.

<script src="../dist/dll/MyDll.alpha.js"></script>

While effective, the configuration is complex and still adds the DLL script to the entry, affecting performance.

Webpack Externals

Externals prevent certain dependencies from being bundled, loading them from a CDN at runtime, simplifying sharing and improving build speed.

<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
module.exports = {
  externals: {
    jquery: 'jQuery'
  }
};

Externals are widely used in micro‑frontend frameworks such as Garfish.

Summary of Solutions

Externals often best address the problem, but have limitations: they may not work for independently rendered pages, they suit only stable libraries, and they can increase the main bundle size.

Webpack 5 Module Federation

What is Module Federation?

It enables applications to expose and consume modules from each other at runtime, defining three roles: Host (consumer), Remote (provider), and Bidirectional‑host (both).

Demo Structure

complete-react-case
├─ component-app (exposes components, both Remote and Host)
│  ├─ src/Button.jsx
│  ├─ src/Dialog.jsx
│  ├─ src/ToolTip.jsx
│  └─ webpack.config.js
├─ lib-app (exposes react, react‑dom as Remote)
│  └─ webpack.config.js
├─ main-app (pure Host, consumes lib‑app and component‑app)
│  └─ webpack.config.js

Module Federation Configuration

/** lib-app/webpack.config.js */
new ModuleFederationPlugin({
  name: 'lib-app',
  filename: 'remoteEntry.js',
  exposes: {
    './react': 'react',
    './react-dom': 'react-dom'
  }
});
/** component-app/webpack.config.js */
new ModuleFederationPlugin({
  name: 'component-app',
  filename: 'remoteEntry.js',
  exposes: {
    './Button': './src/Button.jsx',
    './Dialog': './src/Dialog.jsx',
    './ToolTip': './src/ToolTip.jsx'
  },
  remotes: {
    'lib-app': 'lib_app@http://localhost:3000/remoteEntry.js'
  }
});
/** main-app/webpack.config.js */
new ModuleFederationPlugin({
  name: 'main_app',
  remotes: {
    'lib-app': 'lib_app@http://localhost:3000/remoteEntry.js',
    'component-app': 'component_app@http://localhost:3001/remoteEntry.js'
  }
});

Page Performance

Host modules are not bundled into the main entry, keeping the main bundle small. Remote modules are loaded on demand, so unused code is never fetched.

Each exposed module is packaged separately; if a remote module is not used, it is not loaded, preserving performance even as shared code grows.

Source Code Analysis

The entry file defines webpack_modules for remoteEntry files, which act like manifests linking applications. The bootstrap file is loaded after remoteEntry files, ensuring dependencies are ready before execution.

var __webpack_modules__ = ({
  "webpack/container/reference/component-app": (module, __unused_webpack_exports, __webpack_require__) => {
    // remote reference logic
  },
  "webpack/container/reference/lib-app": (module, __unused_webpack_exports, __webpack_require__) => {
    // remote reference logic
  }
});

Webpack’s runtime loads remotes via __webpack_require__.f.remotes, mapping chunk IDs to remote modules and fetching them as needed.

__webpack_require__.f.remotes = (chunkId, promises) => {
  // resolve remote modules for the chunk
};

RemoteEntry files expose a moduleMap and a get function that returns promises for requested modules.

var moduleMap = {
  "./Button": () => Promise.all([__webpack_require__.e("webpack_container_remote_lib-app_react"), __webpack_require__.e("src_Button_jsx")]).then(() => __webpack_require__("./src/Button.jsx")),
  // other modules …
};
var get = (module, getScope) => {
  return moduleMap[module] ? moduleMap[module]() : Promise.reject(new Error('Module "' + module + '" does not exist'));
};

Integration with Micro‑Frontends

Compared with the Externals approach, Module Federation offers finer‑grained, dynamic sharing without inflating the host bundle, making it a superior solution for micro‑frontend architectures.

Conclusion

Webpack Module Federation enables seamless linking of separate applications via remoteEntry CDN URLs, allowing fine‑grained code sharing beyond traditional micro‑frontend scenarios and across distinct projects.

micro-frontendModule FederationWebpackcode sharing
ELab Team
Written by

ELab Team

Sharing fresh technical insights

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.