Micro Frontends is a very popular topic in today's frontend world. Most of the teams tend to adopt this micro frontend strategy to develop their large and complex web applications, due to many advantages it provides such as,
- Separate, decoupled codebases
- Independent deployment
- Incremental updates
Below diagram showcases some of the important concepts of micro frontends.
Most popular way of developing micro frontends is using HTML5 Web Components (Custom Elements). Angular like web frameworks has extended to support Web Components, while most of other libraries like React, supports it out of the box.
For more information about micro frontends you can refer to
What is Module Federation in Webpack ?
Webpack version 5 comes with a new feature called Module Federation, which helps to share code and dependencies between projects at runtime.
In high level, an application exposes
certain component/s via a separate javascript file, and other application that wishes to use that component, async loads that remote
javascript file and consume that component.
Recent times this feature has changed the way we develop micro frontends.
In this post, I will walk through how you can develop React based micro frontends using Webpack's Module Federation by creating a simple bank application that shows a list of accounts and account details on a selected account.
This is how our final application architecture would look like.
Let's get started..
All of the code in this post can be found here for your references.
Prerequisites
First of all, since this is about React and webpack, you should have a React application configured with Webpack.
Refer to this project if you need help doing that.
Also, as mentioned above, we will need three React projects for our application
-
accounts-summary-app
- Micro frontend that provides the summary of all the accounts -
account-details-app
- Micro frontend that provides details of a selected account -
main-app
- app that hosts above two components. Also acts as a medium to communicate with each other.
Configure ModuleFederationPlugin
in Micro Frontend Projects
ModuleFederationPlugin
is a high level webpack plugin that provides a very convenient way to configure module federation in your projects. Also plugin comes along with webpack library without need of installing another dependency as well.
Responsibility of our micro frontend projects is to expose
a component. So let's add following to webpack.config.js
of accounts-summary-app
project.
import the plugin
const { ModuleFederationPlugin } = require("webpack").container;
configure the plugin
plugins: [
new ModuleFederationPlugin({
name: "AccountsSummaryApp",
filename: "accountsSummaryApp_remote.js",
exposes: {
"./AccountsSummary": "./src/components/AccountsSummary",
},
}),
...
],
-
name
is unique identification of your module. Normally this is the name of your micro frontend project. -
filename
is the name of the javascript file that exposes the components -
exposes
is a map (key and a value) of components that are exposed from this module. (key will act as an alias for the component while the value is where the component located in the project)
Now, let's run this project locally and see what happens.
As you can see, now webpack has bundled our AccountsSummary
component into a separate javascript file, as we instructed in the webpack configuration.
Let's do the same to the account-details-app
project as well
plugins: [
new ModuleFederationPlugin({
name: "AccountDetailsApp",
filename: "accountDetailsApp_remote.js",
exposes: {
"./AccountDetails": "./src/components/AccountDetails",
},
}),
...
],
In case you missed some thing, you can always refer to these two projects
accounts-summary-app
account-details-app
Configure ModuleFederationPlugin
in Host App project.
Like I was explaining before, our host app, main-app
is responsible for loading the components from micro frontend projects.
Like micro frontends configurations defines exposes
, Host app's webpack configuration defines remotes
that tells webpack where to find those external components.
plugins: [
new ModuleFederationPlugin({
remotes: {
AccountsSummaryApp_Remote: "AccountsSummaryApp@http://localhost:9001/accountsSummaryApp_remote.js",
AccountDetailsApp_Remote: "AccountDetailsApp@http://localhost:9002/accountDetailsApp_remote.js",
},
}),
...
],
remotes
is a map (key and value) that defines all the external modules that it consumes. Key will act as an alias for the module and value defines the remote javascript file location for that module.
value should have a special format like below
<Name of the Exposed Module>@<Remote URL of the javascript file>
Now that all the webpack configurations are completed, let's write some javascript code to load external components.
Load External Components to Host App
One of the beautify thing about this webpack module federation is that, developers can not feel a difference between importing a local component from its own project and remote component from an external javascript file.
React code will look like you are lazy loading a component.
const AccountsSummary = React.lazy(() =>
import("AccountsSummaryApp_Remote/AccountsSummary")
);
and use that in your jsx
<Suspense fallback={<h1>Error while loading Account Summary</h1>}>
<AccountsSummary onAccountSelected={handleAccountSelected} />
</Suspense>
One thing to note about the import is that to use same alias that you define in the host application along with the component alias that you defined in your micro frontend project
Communication between components
As mentioned earlier, external components are same as local components in your project. So standard ways of communication should be applicable here as well.
For this application, I have defined a shared state with in the host application and each component communicate via that shared state.
Refer to main-app
to see the code.
Conclusion
This is very beginner level tutorial about how to develop Micro Frontends using Webpack's Module Federation feature. During this post I was able to briefly explain about
- What are Micro Frontends
- What is Webpack's Module Federation Feature
- Configure Micro frontends to expose components
- Configure Host app to use those exposed components
- How to communicate between components
Full working example can be found here.
You can find the full React Template used in this sample bank application here.
That is all for now. Please share you feedbacks. Thank you for reading.
Top comments (2)
Thank you so much for helping with the naming and imports. I was really confused with what to use in exposes {} remotes {} and for importing in the main. Your visual image helped me a lot.
hello I am a bit lost at the general concept, in particular: if I have a shell, and different UI's, let's say A and B (microfrontends), it is possible that the shell packs the React, react-dom, materialui dependencies and the A, B packages don't?
In my experiments, I see that the bundle size of shell, A and B are the same, so I suspect react, react-dom, etc are being bundled in ALL the apps, beating the goal of reducing bundle size by sharing remote dependencies. thanks for clarifying this!