Webpack 5 Module Federation A-Z: React Foodie App (Part I) – basics 👑
Learn how to use Webpack Module Federation in real React application. Find out Micro-Frontends approach, TypeScript, AWS, Redux and many more in this blog series!

Intro
Hey folks! 👋
Welcome to the Webpack Module Federation blog series!
In this blog post, I will show you a practical guide to the Webpack Module Federation with Micro Frontends Architecture.
Since there is a lot to cover, and I want to show you how to use some most popular JavaScript tools, the entire series will be divided into several parts.
This part will be a bootstrapping Module Federation project as simple as possible! At the very beginning, we will learn how to use the Module Federation and a basic overview of Micro Frontends!
Today's application will be the starting point for our React application and will be expanded in the following chapters with new functionalities and tools such as Redux, CSS in JS, Bit and configuration API with Express and NodeJS. We will also implement a ready production solution and deploy the application to AWS using GitHub Actions.
mf/part1
branch, second on mf/part2
and so on.To work comfortably with this application, it would be nice if you already know about Module Federation. In case you haven't heard of it, I will explain it very briefly and give you some useful links that I recommend you check out, as this post will not explain the Module Federation concept in detail.
With that, let's start! 🔥
What is Webpack Module Federation?
Module Federation is a new concept (architecture) of JavaScript application development, which then became a new feature in Webpack as a plugin. This approach is very much related to the idea of Micro Frontends:
Multiple separate builds should form a single application. These separate builds should not have dependencies between each other, so they can be developed and deployed individually.
So, the Module Federation plugin allows us to compose code from a micro-application to another at runtime.
If you still don't quite understand what this means, I encourage you to check out the links below which explain these concepts in more detail, then you'll be ready to move on to our real app example.

React basic application
To best understand the concept of Module Federation, is seen it in action. Especially for this purpose, I have prepared a fully functional simple application based on React. Feel free to clone the repository from the link below and check the code yourself (remember to switch onto the mf/part1
git branch):
Now I will cover the most important parts of this project to find out what is going on there.
Configuration:
This application is set up as a monorepo. I chose to do this mainly for the sake of simplicity, but the great power of Module Federation is that it has no problem migrating it to completely standalone modules. All can be put into their own repository, deployed and run independently.
Let's take a look on package.json
in the root repository:
"workspaces": [
"applications/*",
"api/*" // THIS IS NOT IMPLEMENT YET
],
"scripts": {
"wipe": "rm -fr node_modules packages/**/node_modules",
"build": "yarn workspaces run build",
"start": "concurrently \"wsrun --parallel start\""
},
"dependencies": {
"concurrently": "^7.0.0",
"wsrun": "^5.2.4"
}
concurrently
package.Applications
You can find all micro-frontend micro-applications in the applications
folder, currently there are 6 of them:

App 1—container
App
is the container for all applications. Inside it, we are consuming all other micro-frontend applications.
So let's take a look at the webpack configuration first. There are 3 webpack configuration files in the config
folder:
webpack.common.js
— this file sharing configuration for both dev & prod environment, so every part of configuration which are shared is put here.webpack.dev.js
—all the magic happens here. You can see theModuleFederationPlugin
that uses all other micro-applications in theremote
section:
plugins: [
new ModuleFederationPlugin({
name: 'app',
remotes: {
navigation: 'navigation@http://localhost:8081/remoteEntry.js',
list: 'list@http://localhost:8082/remoteEntry.js',
cookbook: 'cookbook@http://localhost:8083/remoteEntry.js',
cart: 'cart@http://localhost:8084/remoteEntry.js',
footer: 'footer@http://localhost:8085/remoteEntry.js',
},
shared: {
...deps,
react: {
singleton: true,
requiredVersion: deps.react,
},
'react-dom': {
singleton: true,
requiredVersion: deps['react-dom'],
},
},
}),
],
webpack.prod.js
—in theprod
configuration, we will configure the AWS configuration, so we can skip the analysis for now.
ErrorBoundary
Before we go any further, let's take a look at the ErrorBoundary
file, which is a little more customized compared to the basic version found in the React documentation.
export class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch() {}
render() {
if (this.state.hasError) {
return (
<React.Suspense fallback={<div>{this.props.error}</div>}>
{this.props.fallback}
</React.Suspense>
);
}
return (
<React.Suspense fallback={<div>{this.props.loading}</div>}>
{this.props.children}
</React.Suspense>
);
}
}
There are a couple of props: this.props.error
—we pass a loading message in case of error. this.props.fallback
—failure React component in case of error.this.props.loading
- loader element during loading child
I know this can be a little confusing at first, so to clarify, let's look at the order of execution:
this.props.error
—shows during error fallback is loadingthis.props.fallback
-shows the element when component fails andgetDerivedStateFromError
set the error statethis.props.loading
- shows when no error occurs, but normal component is asynchronously loadingthis.props.children
—render children elements
App.jsx
With this knowledge, we can look at the App.jsx
file and see what is going on there. At the very top of the file, you can see the following imports:
import FallbackNavigation from './components/FallbackNavigation';
import FallbackList from './components/FallbackList';
import FallbackCookbook from './components/FallbackCookbook';
import FallbackCart from './components/FallbackCart';
import FallbackFooter from './components/FallbackFooter';
They are all dummy components—placeholders located in the /components
folder only for use in the ErrorBoundary
.
Next part of the more important imports are:
const NavigationLazy = React.lazy(() => import('navigation/NavigationApp'));
const ListLazy = React.lazy(() => import('list/ListApp'));
const CookbookLazy = React.lazy(() => import('cookbook/CookbookApp'));
const CartLazy = React.lazy(() => import('cart/CartApp'));
const FooterLazy = React.lazy(() => import('footer/FooterApp'));
All this import is micro-frontend applications provided by the Module Federation. We import it on demand using the React.lazy
function.
Moving on, there is a routing system that uses the React-router-dom library. All routes are wrapped in React.StrictMode for better debugging, and currently there are 3 routes in <Route />
components: /
, /cookbook
, /shopping-list
.
const App = ({ location }) => {
return (
<React.StrictMode>
<ErrorBoundary
error="Loading fallback navigation"
loading="Loading navigation"
fallback={<FallbackNavigation />}>
<NavigationLazy items={routes} />
</ErrorBoundary>
key
properties passed to the ErrorBoundary
component are responsible for having only one instance of it when using the SPA. The location
object comes from the HOC withRouter
hook that the App
wraps with.Other apps
If we take a closer look at another applications, we can see that these are exposes in the webpack.dev
configuration file:
plugins: [
new ModuleFederationPlugin({
name: 'cart',
filename: 'remoteEntry.js',
exposes: {
'./CartApp': './src/Cart',
},
Most of them are very similar to each other. Only the footer
is prepared as Bidirectional-host and either hosting and consuming another application:
new ModuleFederationPlugin({
name: 'footer',
filename: 'remoteEntry.js',
exposes: {
'./FooterApp': './src/Footer',
},
remotes: {
navigation: 'navigation@http://localhost:8081/remoteEntry.js',
},
Up and running
With this, you can now run the React-foodie app in two different ways.
- All-in-one—you can run all applications simultaneously. When you go to the root of the repository in your terminal and run:
yarn start
, it will run each application on different ports together. You will be able to go tolocalhost:8080
to see theapp
application,localhost:8081
to see thenavigation
and so on … - Separately—you can also run all applications independently. Open each application in a separate terminal window, and run
yarn start
the command for every application.
In the last approach, you can observe our ErrorBoundary
in action. If you don't run one of the Module Federation application, it will be replaced by fallback, and the entire application will still work. And it is amazing! 🤩
Summary:
You did it! 🎉
This is an example of the power of the Module Federation. You already know how it works with tools like react-router-dom, how to make it SPA and handling the accessibility using the ErrorBoundary feature.
This is the first part of the series that will serve as a base for future expansion with other tools, such as Typescript or Bit. So stay tuned!
Thanks for reading ♥️♥️
If this article was helpful, please leave a comment or 👍
…and stay tuned! 👊