Micro Frontends in NextJS with Webpack 5
Webpack 5's Module Federation makes creating micro frontends straightforward. It is also available in NextJS 10+ via an experimental feature flag and it makes integrating micro frontends a breeze. You first build an external micro frontend and deploy it as a "federated module" to your destination of choice. Then you update some configs in your NextJS application and Webpack does the rest. It's kind of magic 🦄.
Oct 21, 2023: I’ve updated my site to use
next-mdx-remote in React
server components, and the previous live micro-frontends that were once where
the following code snippet is, are now broken. I also don’t think anyone
should use micro frontends, and I’m not bothering with fixing these.
I won't go in depth in to what they are (the topic is pretty broad and there are more qualified resources out there), but rather how to add a micro frontend into a NextJS application. To set the mood, here’s an attempt at a brief history recap around web applications and when micro frontends popped up.
Then micro services came into the picture to try and solve organizational problems at scale like enabling greater team autonomy, and some technical issues like improved fault isolation and scalability.
"Microservices" themselves premiered at an event for software architects in 2011, where the term was used to describe a style of architecture that many attendees were experimenting with at the time. Netflix and Amazon were among the early pioneers of microservices.
React was introduced which paved the way for performant client side web applications, and inevitably, monolithic frontends.
ThoughtWorks began assessing a new technique known as "micro frontends" to gain similar benefits that microservices offered, but for the front end.
Webpack 5 was released and its Module Federation feature has opened the doors to new possibilities in the relatively young micro frontend paradigm.
So micro frontends have actually been around for a while now and have been implemented in various ways. One of the newer and exciting additions to the ecosystem is Webpack 5 and its module federation feature.
Note: I may use the terms "micro frontend" and "federated module" interchangeably.
To implement a micro frontend in a NextJS app, you’ll need be on NextJS 10 or higher, and you’ll need a federated module.
My particular micro frontend — a simple button — is a minimally modified fork of the official module-federation bi-directional example.
It’s built then drag 'n' dropped into a public AWS S3 bucket as a quick 'n' easy proof of concept. The bulk of the magic
happens in the
webpack.config.js with the ModuleFederationPlugin.
exposesfields are important as they will be referenced from the consuming application.
NextJS 10 10 comes with Webpack 5 as an experimental feature that you can enable with a feature flag.
NextJS 11 comes with Webpack 5 by default, with zero config
Install NextJS 10+
yarn add next@latest
future.webpack5 flag to
next.config.js, and push a new ModuleFederationPlugin to the plugins list.
remotes field is what allows the consuming application to import a federated module. The
filename values from the federated module are used here:
To import a federated module, you can use the
dynamic helper. Any modules that were included in the
exposes field in the federated module can be used in the import path:
Here is a diagram of the actual micro frontend implementation on this site. For now, the architecture is very basic, but could easily be extended to include things like a dedicated CloudFront distribution for the micro frontend bucket, a Lambda@Edge to server side render the micro frontend, or an Api Gateway for individual micro frontend data requirements.
This is a proof of concept for how easy it is to integrate a federated module / micro frontend into a NextJS application. Despite being an overly simple example, it was a fun exploratory project to go through.
With that being said, there was an initial mental hump that was a bit difficult to get over, likely because I didn't realize how broad the topic/pattern of micro frontends was. Webpack 5 and module federation only make up a tiny chunk of the topic.
The Webpack 5 ecosystem is still in infant stages, and the biggest source of knowledge seems to be blog posts by adventurous early adopters. Stack Overflow posts around the topic are also very limited. 😐
Things I still want to explore firsthand are
- shared react state management across multiple unrelated micro frontends
- creating a micro frontend in a different framework, like Vue, Angular, or React Native Web
- importing said micro frontend
- server side rendering federated modules
- data fetching from individual federated modules
- successfully render a tree of multiple federated React function components that use state hooks
- successfully render a tree of multiple federated React stateful class components
Static type checking is lost with the dynamic importing of federated modules. However, I can see this being solved by publishing static types to npm.
As of this post and the settings shown, I can't seem to get a sane working example of a federated React component that uses a hook, and it seems to be an inherent incompatibility/limitation between ModuleFederationPlugin and some NextJS inner workings.
I've tried multiple options, including some custom loading components, and have successfully rendered a single-depth component
that uses a
useState hook, but the solution was too convoluted to maintain IMO. The ecosystem is still very immature so
I'm hoping for more official patterns around this to come out soon.
In the browser console, you can run this to check for the existence of a federated module.
window.app2.get("./Button").then(factory => factory()).then(mod => console.log(mod))