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
- 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.
- Shared Modules:
- Modules (like libraries or components) that are shared between the host and remote applications.
- 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
- Seamless Integration:
- Webpack handles the integration and dynamic loading of remote apps, allowing you to seamlessly combine different applications into one UI.
- 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.
- 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.
- Efficient Code Splitting:
- With Module Federation, you can split your frontend into smaller chunks that are loaded only when needed, improving performance.
- 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
- 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.
- Performance Overhead:
- Dynamically loading remote components can introduce performance overhead, especially if there are many small bundles to load.
- Version Compatibility:
- If remote micro frontends use different versions of libraries or frameworks, managing compatibility between those versions can be challenging.