Intro
Module federation is a new feature that come with webpack 5 and it allows developers to use components from other builds (this might be know as micro-frontend). It could also enable other possibilities like have an isolated team that works on reusable components that can be shared across different projects without paying the price of installing a whole library. In addition if a bug is found on one component that bug could be solved once and all teams that use that component will see the benefit immediately.
Getting Started
Create a project that will hold our example application, to do that just open a terminal and go to the desired path then type the following commands:
1mkdir my-awesome-example2cd my-awesome-example3npm init
Answer all the question prompted and then is time to install webpack, the following command could be used for that task:
1npm install --save-dev webpack
or if you are using yarn you can do this
1yarn add webpack --dev
The example is going to based on React but it could be used with any other library or framework. For reference and example you can take a look at the following repository.
In this example we are going to build a component that is going to be used later by another application, so once we have webpack installed we need to install React to do that just type the following commands
1npm install --save react react-dom2npm install --save-dev @babel/core @babel/preset-react babel-loader bundle-loader \3 html-webpack-plugin serve webpack-cli webpack-dev-server
Those dev dependencies are used to build the react application, if you are interested in what each of those does take a look at the Additional section at the end of the post
The next step is to replace the scripts on the package.json with the following scripts, those
scripts are going to be used to serve the application on development mode, and also create a build
that can be used on production.
File: package.json
1{2 # ... content3 "scripts": {4 "start": "webpack-cli serve",5 "build": "webpack --mode production",6 "serve": "serve dist -p 3000",7 "clean": "rm -rf dist"8 }9 # ... more content10}
Configure webpack & build React app
The next thing that we need to do is to configure webpack to serve the application we are going to
build. To do that just create a file named webpack.config.js on the root of the project, that file
should have the following content
1const HtmlWebpackPlugin = require('html-webpack-plugin')2const path = require('path')34module.exports = {5 entry: './src/index',6 mode: 'development',7 devServer: {8 contentBase: path.join(__dirname, 'dist'),9 port: 3000,10 },11 output: {12 publicPath: 'auto',13 },14 module: {15 rules: [16 {17 test: /bootstrap\.js$/,18 loader: 'bundle-loader',19 options: {20 lazy: true,21 },22 },23 {24 test: /\.jsx?$/,25 loader: 'babel-loader',26 exclude: /node_modules/,27 options: {28 presets: ['@babel/preset-react'],29 },30 },31 ],32 },33 plugins: [34 new HtmlWebpackPlugin({35 template: './public/index.html',36 }),37 ],38}
Something interesting to notice is that the bootstrap.js file is going to be loaded with
bundle-loader and it’s going to be loaded lazy
1import bootstrap from './bootstrap'2bootstrap()
Then a file named bootstrap.js needs to be created
1import React from 'react'2import ReactDOM from 'react-dom'3import App from './App'45ReactDOM.render(<App />, document.getElementById('root'))
And finally App.js
1import React from 'react'23const App = () => (4 <div>5 <h1>Hello from App 1</h1>6 </div>7)89export default App
Additionally we need to create the following file public/index.html with this content
1<html>2 <head> </head>3 <body>4 <div id="root"></div>5 </body>6</html>
This file is going to be used by webpack to generate the html and inject the different components of the application.
Configure application that will hold our components
So far we have a normal React application, now we are going to configure that application to hold
our components and share them with other applications to do that we need to use the new plugin from
webpack called ModuleFederationPlugin. Open webpack.config.js and add the following content
1// ... Other content2const { ModuleFederationPlugin } = require("webpack").container;34module.exports = {5 // ... webpack config6 plugins: [7 new ModuleFederationPlugin({8 name: "awesome",9 library: { type: "var", name: "awesome" },10 filename: "remoteEntry.js",11 exposes: {12 "./HomePage": "./src/HomePage",13 },14 shared: { react: { singleton: true }, "react-dom": { singleton: true } },15 }),16 // ... other plugins17 ],18};
This configuration says that we are going to create a shared module that it’s name is ‘awesome’ and
that module is going to contain (or expose) a component named HomePage, and react and
react-dom are going to be shared by both so there is no need to include that on the build.
Lets create the component that we are going to share, create a file named HomePage.js under the
src folder and add the following content to it:
1import React from 'react'23const HomePage = ({ title }) => (4 <h1>Hello {title}, from awesome</h1>5)67export default HomePage
Create app that will use shared components
On the same way that we create the app that is going to share the components we need to create the
other app that is going to use the shared components (you can also copy the current project folder
and use it as an starting point cp -r ../my-awesome-example ../use-my-awesome-example from the
project root). If you don’t copy the folder follow the steps from Getting Started until
Configure application that will hold our components
Then we need to configure the app to use the components from the other project
Once again lets go to the webpack config and modify or add (modify if you copy the project) the
ModuleFederationPlugin under the plugins section like this:
1// ... Other content2const { ModuleFederationPlugin } = require("webpack").container;34module.exports = {5 // ... webpack config6 plugins: [7 new ModuleFederationPlugin({8 name: "use-remote-components",9 remotes: {10 awesome: "awesome@http://localhost:3002/remoteEntry.js",11 },12 shared: { react: { singleton: true }, "react-dom": { singleton: true } },13 }),14 // ... other plugins15 ],16};
Make sure to start the project on the port 3002 (npm run start -- --port 3002)
Then we need to create a new component on the new application, open src/App.js and add the
following content
1import React from 'react'23const RemoteHomePage = React.lazy(() => import("awesome/HomePage"));45const App = () => (6 <div>7 <React.Suspense fallback="Loading ...">8 <RemoteHomePage title="Awesome component" />9 </React.Suspense>10 </div>11)1213export default App
Make sure to add the Suspense otherwise the RemoteHomePage is not going to work. Start the
project and you will see the component from the awesome app rendered in the app we just created.