Webpack Module Federation for Micro Frontends

Loading

Webpack Module Federation is a powerful feature introduced in Webpack 5 that allows multiple applications to share code dynamically at runtime. This feature is particularly useful in the context of Micro Frontends, where different frontend applications (often managed by different teams) can be integrated into a single user experience without tightly coupling them together.

Why Use Webpack Module Federation?

In a traditional monolithic frontend setup, all the code is bundled together, and apps rely on large bundles that contain everything. As frontend applications grow, the codebase becomes difficult to maintain, deploy, and scale. Micro Frontends tackle these issues by breaking the application into smaller, independently deployable pieces.

With Webpack Module Federation, you can share code (like components, utilities, or even entire modules) between micro frontends without needing to replicate or re-bundle the same code.

Key Concepts of Webpack Module Federation

  1. Host and Remote Apps:
    • Host App: The main application that loads micro frontends.
    • Remote App: A micro frontend application that is loaded by the host app.
  2. Shared Modules:
    • Modules (like libraries or components) that are shared between the host and remote applications.
  3. Dynamic Loading:
    • Instead of bundling all dependencies into one large file, Module Federation allows the dynamic loading of remote components at runtime, which keeps the initial bundle size small.

How Webpack Module Federation Works

Basic Workflow:

  • The remote app exposes its components (modules).
  • The host app consumes those exposed modules at runtime by fetching them from the remote app.

The key concept is that both the host and remote apps are configured using Webpack’s ModuleFederationPlugin, which facilitates sharing code between them.

Step-by-Step Implementation of Webpack Module Federation for Micro Frontends

We will walk through a basic example of how to set up two React applications where one acts as the host and the other as the remote.

1. Setting Up the Remote Application

Let’s assume that we have a remote app called RemoteApp. In this app, we will expose a simple component called Header that the host app will use.

1.1: Install Dependencies in RemoteApp

Start by creating a new React app (using Create React App or any boilerplate you prefer) and install Webpack 5 if it’s not already installed.

npx create-react-app remote-app
cd remote-app
npm install webpack@5 webpack-cli webpack-dev-server --save-dev
npm install react-router-dom
1.2: Configure Webpack in RemoteApp

Create a webpack.config.js file to set up the Module Federation Plugin for the remote app.

// webpack.config.js for remote app
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  mode: 'development',
  devServer: {
    port: 3001, // Port for the remote app
    contentBase: './dist',
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'remoteApp',  // Name of the remote app
      filename: 'remoteEntry.js', // Expose this file
      exposes: {
        './Header': './src/Header',  // Expose Header component
      },
      shared: ['react', 'react-dom'], // Shared dependencies (to avoid duplication)
    }),
  ],
};
1.3: Expose the Header Component in RemoteApp

Now, create a simple Header component that we want to share with the host app:

// src/Header.js in remote-app
import React from 'react';

const Header = () => {
  return <h1>Hello from Remote App!</h1>;
};

export default Header;
1.4: Update package.json in RemoteApp

To enable Webpack’s build process and serve your app, add the following to your package.json:

"scripts": {
  "start": "webpack serve --config webpack.config.js",
  "build": "webpack --config webpack.config.js"
}

Now, you can run the remote app using:

npm start

2. Setting Up the Host Application

Next, create the HostApp that will consume the exposed Header component from RemoteApp.

2.1: Install Dependencies in HostApp

Create a new React app (or use your existing app) for the host:

npx create-react-app host-app
cd host-app
npm install webpack@5 webpack-cli webpack-dev-server --save-dev
npm install react-router-dom
2.2: Configure Webpack in HostApp

Now, configure the Webpack Module Federation Plugin in the host app.

// webpack.config.js for host app
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  mode: 'development',
  devServer: {
    port: 3000, // Port for the host app
    contentBase: './dist',
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'hostApp',  // Name of the host app
      remotes: {
        remoteApp: 'remoteApp@http://localhost:3001/remoteEntry.js', // Define where to load remote app from
      },
      shared: ['react', 'react-dom'], // Shared dependencies (to avoid duplication)
    }),
  ],
};
2.3: Consume Remote Component in HostApp

Now, you can consume the Header component exposed by the remote app. In the host app, dynamically import the remote component.

// src/App.js in host-app
import React, { Suspense } from 'react';

// Dynamically import Header from remote app
const Header = React.lazy(() => import('remoteApp/Header'));

function App() {
  return (
    <div>
      <Suspense fallback={<div>Loading Header...</div>}>
        <Header />
      </Suspense>
      <h1>Hello from Host App!</h1>
    </div>
  );
}

export default App;
2.4: Update package.json in HostApp

Similar to the remote app, update the package.json in the host app to use Webpack:

"scripts": {
  "start": "webpack serve --config webpack.config.js",
  "build": "webpack --config webpack.config.js"
}

Run the host app with:

npm start

Now, when you open http://localhost:3000, the host app will load and display both its own content (HostApp) and the dynamically loaded Header component from RemoteApp.

3. Testing and Running Both Apps

  • RemoteApp should be running on http://localhost:3001
  • HostApp should be running on http://localhost:3000

The Header component from RemoteApp will be fetched dynamically into the HostApp and rendered.

Advantages of Webpack Module Federation for Micro Frontends

  1. Seamless Integration:
    • Webpack handles the integration and dynamic loading of remote apps, allowing you to seamlessly combine different applications into one UI.
  2. Independent Development and Deployment:
    • Each micro frontend (remote app) can be developed, built, and deployed independently, making it easier to scale and update individual pieces of the frontend.
  3. Shared Dependencies:
    • You can share common libraries and dependencies (like React or ReactDOM), ensuring that they aren’t duplicated in each micro frontend’s bundle.
  4. Efficient Code Splitting:
    • With Module Federation, you can split your frontend into smaller chunks that are loaded only when needed, improving performance.
  5. Flexibility in Framework Choices:
    • Micro frontends can be built using different frameworks or versions, providing flexibility in how each feature or module is developed.

Challenges of Webpack Module Federation

  1. Complexity in Configuration:
    • Setting up Module Federation can be tricky, especially when integrating with multiple micro frontends, as it requires careful management of shared dependencies, versioning, and URLs.
  2. Performance Overhead:
    • Dynamically loading remote components can introduce performance overhead, especially if there are many small bundles to load.
  3. Version Compatibility:
    • If remote micro frontends use different versions of libraries or frameworks, managing compatibility between those versions can be challenging.

Leave a Reply

Your email address will not be published. Required fields are marked *