Webpack module federation: a new choice for micro front-end architecture

Tianya School
6 min readDec 9, 2024

--

Webpack Module Federation is a revolutionary feature introduced in Webpack 5 that completely changes the way micro-frontend architecture is implemented. Module Federation allows different web applications (or micro-frontend applications) to dynamically share code at runtime without physical sharing in the traditional packaging or publishing process. This means that each micro-application can be independently developed, built, and deployed, while also being able to easily share components, libraries, and even business logic.

Basic Concepts

  • Container application (Container): As the host of the micro-frontend architecture, responsible for loading and coordinating various micro-applications.
  • Remote application (Remote): An independent micro-application that can expose its own modules to other applications for use, or consume modules from other applications.

Implementation steps

1. Container application configuration

In the webpack.config.js of the container application, use ModuleFederationPlugin to declare the source of the remote micro-application.

// webpack.config.js (Container)
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
// ...Other Configuration
plugins: [
new ModuleFederationPlugin({
name: 'container',
remotes: {
app1: 'app1@http://localhost:3001/remoteEntry.js',
app2: 'app2@http://localhost:3002/remoteEntry.js',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
},
}),
],
};

Here, the remotes field specifies the name of the remote micro-app and its remote entry file URL. The shared configuration indicates which modules should be shared as singletons, such as React and ReactDOM, to avoid duplicate loading.

2. Remote application configuration

In each remote application’s webpack.config.js, ModuleFederationPlugin is also used, but this time to expose its own modules.

// webpack.config.js (Remote App1)
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
// ...Other Configuration
plugins: [
new ModuleFederationPlugin({
name: 'app1',
filename: 'remoteEntry.js',
exposes: {
'./MyComponent': './src/components/MyComponent',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
},
}),
],
};

The exposes field defines which modules will be exposed externally. In this example, the MyComponent component can be imported and used from the container application or other micro applications.

3. Consuming remote modules

In the container application or another remote application, you can directly import the remotely exposed modules.

// In a component of Container or another Remote App
import MyComponent from 'app1/MyComponent';

function App() {
return (
<div>
<h1>Container App</h1>
<MyComponent />
</div>
);
}
export default App;

Advantages

  • Independent development and deployment: Each micro-application can be independently developed, built, and deployed, which improves development efficiency and deployment flexibility.
  • On-demand loading: Only when a module is actually used, the corresponding remote code will be loaded, which optimizes the first screen loading time and overall performance.
  • Version management and isolation: Each micro-application can freely upgrade its dependencies to avoid version conflicts.
  • Easy to maintain and expand: The loose coupling feature of module federation makes it easy and quick to add or remove micro-applications.

Webpack module federation provides an efficient and flexible solution for the development and maintenance of modern Web applications by simplifying the code sharing mechanism in the micro-frontend architecture.

Actual combat case: Building a simple micro-frontend application

Let’s use a simple example to demonstrate how to use Webpack module federation to build two micro-applications: a container application and a remote application.

1. Create a container application

First, create a new React application as a container application:

npx create-react-app container-app
cd container-app

Install webpack and webpack-cli (note that since create-react-app already includes Webpack, it is usually not necessary to install it separately. This is only for demonstration purposes):

npm install webpack webpack-cli --save-dev

Modify package.json and add a startup script to configure Webpack:

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

Create webpack.config.js and configure Module Federation Plugin:

// webpack.config.js (Container App)
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
// ...Other Configuration
plugins: [
new HtmlWebpackPlugin({ template: './public/index.html' }),
new ModuleFederationPlugin({
name: 'containerApp',
remotes: {
remoteApp: 'remoteApp@http://localhost:3010/remoteEntry.js',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
},
}),
],
};

2. Create a remote application

Create a remote application in another directory:

npx create-react-app remote-app
cd remote-app

Similarly, modify package.json, add the startup script, and install webpack and webpack-cli (for example only):

npm install webpack webpack-cli --save-dev

Configure the Module Federation Plugin in remote-app’s webpack.config.js to expose the component:

// webpack.config.js (Remote App)
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

module.exports = {
// ...Other Configuration
plugins: [
new ModuleFederationPlugin({
name: 'remoteApp',
filename: 'remoteEntry.js',
exposes: {
'./MyWidget': './src/MyWidget',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
},
}),
],
};

Create the MyWidget.js component in the remote-app/src directory:

// MyWidget.js
import React from 'react';

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

3. Container application consumes remote components

Go back to container-app and import remote components where needed:

// container-app/src/App.js
import React from 'react';
import MyWidget from 'remoteApp/MyWidget';

function App() {
return (
<div className="App">
<header className="App-header">
<MyWidget />
</header>
</div>
);
}
export default App;

4. Start the application

Start two applications separately:

# In the remote application directory
npm start --port 3010
# In the container application directory
npm start

Now, when you access the container application in the browser, you should see that the components from the remote application are successfully loaded and displayed.

Advanced Usage and Best Practices

1. Dynamic loading and lazy loading

In a real project, you may want to dynamically load remote applications based on user behavior or specific conditions. Webpack module federation supports asynchronous loading, just use the import() function when importing.

// container-app/src/App.js
import React, { lazy, Suspense } from 'react';
const MyWidget = lazy(() => import('remoteApp/MyWidget'));

function App() {
return (
<div className="App">
<Suspense fallback={<div>Loading...</div>}>
<MyWidget />
</Suspense>
</div>
);
}
export default App;

In this way, the MyWidget component will be loaded on demand when needed, improving the first screen loading speed.

2. Version management and dependency management

In the micro-frontend architecture, ensuring the compatibility of dependency versions between different applications is key. Using the shared configuration of ModuleFederationPlugin, you can specify the version range and loading strategy of the shared module (for example, singleton, strictVersion, etc.).

// webpack.config.js
new ModuleFederationPlugin({
// ...
shared: {
react: { version: '^17.0.0', singleton: true },
'react-dom': { version: '^17.0.0', singleton: true },
},
}),

3. Routing Integration

In the micro-frontend architecture, routing management is an important component. You can use libraries like react-router-dom, combined with Microfrontends-Router or custom solutions to implement cross-application routing jumps.

// container-app/src/Routes.js
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import App1 from './App1';
import App2 from './App2';

function Routes() {
return (
<Router>
<Switch>
<Route path="/app1" component={App1} />
<Route path="/app2" component={App2} />
</Switch>
</Router>
);
}
export default Routes;

4. State management

For shared state requirements, you can use state management libraries such as Redux, MobX, or Context API, or state management libraries designed specifically for micro-frontends such as single-spa-redux, qiankun's store solution, etc.

5. Shared services and public libraries

In addition to components, you can also share services and public libraries. For example, create a dedicated remote application to provide API services, or share a public HTTP library.

// webpack.config.js (Remote App for Services)
new ModuleFederationPlugin({
name: 'services',
filename: 'remoteEntry.js',
exposes: {
'./ApiService': './src/services/ApiService',
'./HttpLibrary': './src/libs/http-library',
},
shared: {
// ...Other shared libraries
},
}),

6. Error handling and logging

In order to ensure the stable operation of the micro-frontend application, global error capture and logging need to be implemented. You can use window.onerror, try...catch statements, or use a dedicated logging library such as log4js.

// container-app/src/index.js
window.onerror = function (errorMessage, fileName, lineNumber, columnNumber, error) {
// Record error information
console.error(errorMessage, fileName, lineNumber, columnNumber, error);
// ...other processing logic
return true; // Prevent browser default error handling
};

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Tianya School
Tianya School

Written by Tianya School

❤️ • Full Stack Developer 🚀 • Building Web Apps 👨‍💻 • Learning in Public 🤗 • Web3 Developer ⚡ • Freelance Dev 💼 • DM for Work / Collabs 💬

No responses yet

Write a response