Next.js + Module Federation
https://github.com/tkssharma/nextjs-micro-frontends
Module Federation
Module Federation is a Webpack 5 feature that has arrived to make it possible to share parts of an application to another at runtime. This makes it possible for multiple applications compiled with webpack to repurpose parts of their code as the user interacts with them, which takes us to the next step.
Next.js + Module Federation
Let’s start with our first example of this article where we talk about an eCommerce application, now imagine that our marketing team decides to create a mega Black Friday campaign and decides to change several parts of our application by inserting different components with dynamic banners, carousels, countdowns, themed offers, etc… this would probably be a headache for all teams responsible for our microfrontend applications since each one would have to implement the new requirements of the marketing team in their projects and that would have to be very well tested and synchronized so that everything went right and nothing could be released ahead of time… Anyway, all this could easily generate a lot of work and a lot of headache for the team, but that’s where the very powerful Module Federation comes in.
Thanks to it, only one team would be in responsible for developing the new components along with their respective logic, and the rest of the team would only be responsible for implementing the use of these new complements, which could bring with them, hooks, components in React, among others.
Unfortunately, implementing and using the Module Federation features of Webpack with Next.js is not that easy, as you would need to deeply understand how both tools work to be able to create a solution that facilitates the integration between the two. Fortunately, there is already a solution and has several features including support for SSR (server-side rendering), these tools are called nextjs-mf and nextjs-ssr and together we are going to explore a proof-of-concept application that I created to show you the power of these tools together.
⚠️ Attention: for the application to work with Module Federation features you need to have access to the nextjs-mf or nextjs-ssr plugin which currently requires a paid license!
Let’s start with our first example of this article where we talk about an eCommerce application, now imagine that our marketing team decides to create a mega Black Friday campaign and decides to change several parts of our application by inserting different components with dynamic banners, carousels, countdowns, themed offers, etc… this would probably be a headache for all teams responsible for our microfrontend applications since each one would have to implement the new requirements of the marketing team in their projects and that would have to be very well tested and synchronized so that everything went right and nothing could be released ahead of time… Anyway, all this could easily generate a lot of work and a lot of headache for the team, but that’s where the very powerful Module Federation comes in.
Thanks to it, only one team would be in responsible for developing the new components along with their respective logic, and the rest of the team would only be responsible for implementing the use of these new complements, which could bring with them, hooks, components in React, among others.
Unfortunately, implementing and using the Module Federation features of Webpack with Next.js is not that easy, as you would need to deeply understand how both tools work to be able to create a solution that facilitates the integration between the two. Fortunately, there is already a solution and has several features including support for SSR (server-side rendering), these tools are called nextjs-mf and nextjs-ssr and together we are going to explore a proof-of-concept application that I created to show you the power of these tools together.
⚠️ Attention: for the application to work with Module Federation features you need to have access to the nextjs-mf or nextjs-ssr plugin which currently requires a paid license!
Microfrontends using Next.js and Module Federation
Microfrontends using Next.js and Module Federation
https://www.npmjs.com/package/@module-federation/nextjs-mf
https://github.com/tkssharma/nextjs-micro-frontends
how to expose components from Guest application
import Image from 'next/image'
import styles from '../styles/Mario.module.css'
const Mario = () => {
return (
<main className={styles.main}>
<Image
src="https://upload.wikimedia.org/wikipedia/en/a/a9/MarioNSMBUDeluxe.png"
alt="Mario"
width={240}
height={413}
/>
<h1 className={styles.title}>
G'day! I'm Mario, a microfrontend.
</h1>
<span>I'm hosted at <a target="_blank" href="https://mf-micro-front-end-activate.vercel.app">https://mf-micro-front-end-activate.vercel.app</a></span>
</main>
)
}
export default Mario
Here in next.config
we are exposing components mario from this application,
same we can do from another front end application and render both on different routes on container Host application
webpack: (config, options) => {
const { isServer } = options;
const mfConf = {
mergeRuntime: true, //experimental
name: "app2",
library: {
type: config.output.libraryTarget,
name: "app2",
},
filename: "static/runtime/app2remoteEntry.js",
remotes: {
},
exposes: {
"./mario": "./components/mario",
},
};
Micro Front end 01
We are building a simple application which will render componnets from two different other front end application,
- we have one Host application (container app)
- we have guest application from which we will render Mario component
- we have another application from which we will render another components
const {
withModuleFederation,
} = require("@module-federation/nextjs-mf");
module.exports = {
future: { webpack5: true },
images: {
domains: ['upload.wikimedia.org'],
},
webpack: (config, options) => {
const { isServer } = options;
const mfConf = {
mergeRuntime: true, //experimental
name: "app2",
library: {
type: config.output.libraryTarget,
name: "app2",
},
filename: "static/runtime/app2remoteEntry.js",
remotes: {
},
exposes: {
"./mario": "./components/mario",
},
};
config.cache = false;
withModuleFederation(config, options, mfConf);
return config;
},
Micro Front end 02
const {
withModuleFederation,
} = require("@module-federation/nextjs-mf");
module.exports = {
future: { webpack5: true },
images: {
domains: ['upload.wikimedia.org'],
},
webpack: (config, options) => {
const { isServer } = options;
const mfConf = {
mergeRuntime: true, //experimental
name: "app1",
library: {
type: config.output.libraryTarget,
name: "app1",
},
filename: "static/runtime/app1RemoteEntry.js",
remotes: {
},
exposes: {
"./luigi": "./components/luigi",
},
};
config.cache = false;
withModuleFederation(config, options, mfConf);
return config;
},
Shell Container Micro Front end
const {
withModuleFederation,
} = require("@module-federation/nextjs-mf");
module.exports = {
future: { webpack5: true },
images: {
domains: ['upload.wikimedia.org'],
},
webpack: (config, options) => {
const mfConf = {
name: "shell",
library: {
type: config.output.libraryTarget,
name: "shell",
},
remotes: {
app1: "app1",
app2: "app2",
},
exposes: {
},
};
config.cache = false;
withModuleFederation(config, options, mfConf);
return config;
},
Container app page route
import dynamic from 'next/dynamic'
const RemoteMario = dynamic(
() => import('app2/mario'),
{ ssr: false }
)
const App1 = () => (<RemoteMario />)
export default App1
we have to import bundles from other applications in _document.js
import Document, { Html, Head, Main, NextScript } from "next/document";
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
}
render() {
return (
<Html>
<script src="http://localhost:3002/_next/static/runtime/app1RemoteEntry.js" />
<script src="http://localhost:3001/_next/static/runtime/app2RemoteEntry.js" />
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
How to Test setup
cd micro-front-end-activate
npm install
nbpm run build
npm run dev
cd micro-front-end-main
npm install
npm run build
npm run dev
cd micro-front-end-shell
npm install
npm run build
npm run dev
Testing setup
- localhost:3000/maruo will render mario component from app2
- localhost:3000/luigi will render luigi component from app1
Comments