Harnessing the Power of Module Federation for Micro Frontends

Harnessing the Power of Module Federation for Micro Frontends

Introduction to Module Federation

Module Federation is a powerful feature introduced in Webpack 5 that allows you to share code between multiple JavaScript applications at runtime. This feature is particularly useful for micro frontend architectures, enabling independent deployment, development, and code reuse across different parts of an application.

By leveraging Module Federation, development teams can benefit from:

  1. Code Reusability: Share common components, utilities, and libraries between multiple applications or micro frontends, reducing duplication and improving maintainability.
  2. Independent Deployments: Deploy individual parts of an application without impacting other components, allowing for faster development and release cycles.
  3. Loose Coupling: Create a more maintainable and scalable architecture by reducing the dependencies between different parts of an application.

Getting Started with Module Federation

To get started with Module Federation, you need to have Webpack 5 installed in your project. You can add the required configuration to your `webpack.config.js` file to define how your application should share or consume modules.

Configuring the Host Application

First, configure the host application that will consume the shared modules. In the `webpack.config.js` file, add the following configuration:

  1. const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
  2.  
  3. module.exports = {
  4. // ...
  5. plugins: [
  6. new ModuleFederationPlugin({
  7. name: "host",
  8. remotes: {
  9. remoteApp: "remoteApp@http://localhost:3001/remoteEntry.js",
  10. },
  11. }),
  12. ],
  13. // ...
  14. };

Here, we define a remote application named `remoteApp` with its `remoteEntry.js` file hosted at http://localhost:3001/remoteEntry.js. This file will be generated by the remote application's Webpack configuration and will include the information required to consume the shared modules.

Configuring the Remote Application

Next, configure the remote application that will share its modules. In the `webpack.config.js` file, add the following configuration:

  1. const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
  2.  
  3. module.exports = {
  4. // ...
  5. plugins: [
  6. new ModuleFederationPlugin({
  7. name: "remoteApp",
  8. filename: "remoteEntry.js",
  9. exposes: {
  10. "./SharedComponent": "./src/SharedComponent",
  11. },
  12. }),
  13. ],
  14. // ...
  15. };

In this configuration, we define the remote application's name as `remoteApp` and specify the `filename` as `remoteEntry.js`. The exposes object lists the modules that should be shared with other applications. In this example, we're sharing a module called `SharedComponent`.

Consuming Shared Modules

To consume the shared modules in the host application, you can use the `import()` syntax. This will load the module asynchronously at runtime:

  1. import("remoteApp/SharedComponent").then((SharedComponent) => {
  2. // Use the shared component here
  3. });

Best Practices for Module Federation

When using Module Federation, it's essential to follow best practices to ensure a smooth implementation:

  1. Version Management: Keep the versions of shared dependencies in sync between applications to prevent runtime issues. You can use tools like Dependabot or Renovate to automate this process.
  2. Error Handling: Implement proper error handling in your applications to handle any issues that may arise due to incompatible shared modules or network errors. This can help maintain a stable user experience even when errors occur.
  3. Performance Optimization: Be mindful of the impact of sharing large modules on your application's performance. Share only the necessary code, and consider using code-splitting to minimize the initial loading time for your users.
  4. Testing: Make sure to thoroughly test the integration between the host and remote applications. This includes testing the shared modules in isolation, as well as testing their integration within the host application.
  5. Documentation: Maintain clear documentation for the shared modules and their usage. This will help other developers understand how to consume the shared modules and ensure that the shared code is used consistently across applications.

Module Federation in Action

Let's take a look at a simple example of how to use Module Federation with React components. We'll create two applications: app1 (the host application) and app2 (the remote application).

Setting Up the Remote Application (app2)

1. Create a new React application using `create-react-app`.

  1. npx create-react-app app2

2. Install Webpack 5 as a dependency.

  1. npm install webpack@5 webpack-cli@4 webpack-dev-server@4 --save-dev

3. Create a `webpack.config.js` file in the root folder of `app2` and add the following configuration:

  1. const path = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
  4.  
  5. module.exports = {
  6. entry: './src/index.js',
  7. output: {
  8. path: path.resolve(__dirname, 'dist'),
  9. publicPath: 'http://localhost:3002/',
  10. },
  11. devServer: {
  12. contentBase: path.join(__dirname, 'dist'),
  13. port: 3002,
  14. },
  15. plugins: [
  16. new HtmlWebpackPlugin({
  17. template: './public/index.html',
  18. }),
  19. new ModuleFederationPlugin({
  20. name: 'app2',
  21. filename: 'remoteEntry.js',
  22. exposes: {
  23. './Button': './src/Button',
  24. },
  25. }),
  26. ],
  27. };

4. Create a new `Button.js` file in the `src` folder of `app2` and add a simple React component:

  1. import React from 'react';
  2.  
  3. const Button = () => {
  4. return <button>Click me!</button>;
  5. };
  6.  
  7. export default Button;

5. Start the development server for `app2`:

  1. npx webpack serve

Setting Up the Host Application (app1)

1. Create a new React application using create-react-app.

  1. npx create-react-app app1

2. Install Webpack 5 as a dependency.

  1. npm install webpack@5 webpack-cli@4 webpack-dev-server@4 --save-dev

 3. Create a `webpack.config.js` file in the root folder of `app1` and add the following configuration:

  1. const path = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
  4.  
  5. module.exports = {
  6. entry: './src/index.js',
  7. output: {
  8. path: path.resolve(__dirname, 'dist'),
  9. publicPath: 'http://localhost:3001/',
  10. },
  11. devServer: {
  12. contentBase: path.join(__dirname, 'dist'),
  13. port: 3001,
  14. },
  15. plugins: [
  16. new HtmlWebpackPlugin({
  17. template: './public/index.html',
  18. }),
  19. new ModuleFederationPlugin({
  20. name: 'app1',
  21. remotes: {
  22. app2: 'app2@http://localhost:3002/remoteEntry.js',
  23. },
  24. }),
  25. ],
  26. };

4. Update the `src/App.js` file in `app1` to load the shared `Button` component from `app2`: 

  1. import React, { useState, useEffect } from 'react';
  2.  
  3. const App = () => {
  4. const [Button, setButton] = useState(null);
  5.  
  6. useEffect(() => {
  7. import('app2/Button').then((module) => {
  8. setButton(() => module.default);
  9. });
  10. }, []);
  11.  
  12. return (
  13. <div>
  14. <h1>App 1</h1>
  15. {Button && <Button />}
  16. </div>
  17. );
  18. };
  19.  
  20. export default App;

5. Start the development server for `app1`:

  1. npx webpack serve

Now, when you open `app1` in your browser at http://localhost:3001, you should see the `Button` component from `app2` being displayed.

Conclusion

Module Federation is a powerful feature in Webpack 5 that simplifies code sharing between JavaScript applications, making it a great fit for micro frontend architectures. By leveraging this tool, you can build more scalable and maintainable applications with reduced dependencies and improved development workflows. Don't forget to follow best practices, such as version management, error handling, performance optimization, testing, and documentation, to ensure a successful implementation.

We use cookies to improve your browsing experience. By continuing to use this website, you consent to our use of cookies. Learn More