I followed below steps to achieve module federation in next js.
- Create a component Insurance_Detail.tsx in repository cm-insurance-web inside src/node/components folder, which will be exposed. Below is the next.config.js file.
const assetPrefix = '/jobs-assets';
const nextConfig = {
assetPrefix,
env: {
assetPrefix
},
experimental: {
images: {
unoptimized: true
}
},
reactStrictMode: true,
webpack5: true,
srcDir: 'src/node/',
//distDir: 'build',
webpack: (config, options) => { // webpack configurations
config.plugins.push(
new options.webpack.container.ModuleFederationPlugin({
name:"InsuranceA",
filename: "static/chunks/pages/cm_insurance_web.js", // remote file name which will used later
remoteType: "var",
exposes: { // expose all component here.
**"./InsuranceDetail": "./components/Insurance_Details.tsx"**
},
shared: [
{
react: {
eager: true,
singleton: true,
requiredVersion: false,
}
},
{
"react-dom": {
eager: true,
singleton: true,
requiredVersion: false,
}
},
]
})
)
config.cache = false;
config.output.publicPath = 'http://localhost:3000/_next/';
return config
}
}
module.exports = nextConfig
When we build repo cm-insurance-web using command npm run build, we can see javascript file is being created inside src/node/.next/static/chunks/pages/cm_insurance_web.js. This project repo is running on 3000 port on localhost.
Now need to consume this javascript in other repository let say cm-job-board-web. Let's create consumer app. Below is the next.config.js file for it
/** @type {import('next').NextConfig} */
const assetPrefix = '/jobs-assets';
const path = require('path');
const nextConfig = {
assetPrefix,
env: {
assetPrefix
},
basePath: '/search-jobs',
experimental: {
images: {
unoptimized: true
}
},
reactStrictMode: true,
srcDir: 'src/node/',
webpack: (config, options) => {
config.plugins.push(
new options.webpack.container.ModuleFederationPlugin({
name:"jobboardWeb",
filename: "static/chunks/cm_job_board_web.js",
remoteType: "var",
remotes: {
InsuranceA: JSON.stringify('InsuranceA@http://localhost:3000/jobs-assets/_next/static/chunks/pages/cm_insurance_web.js')
},exposes: {},
shared: [
{
react: {
eager: true,
singleton: true,
requiredVersion: false,
}
},
{
"react-dom": {
eager: true,
singleton: true,
requiredVersion: false,
}
},
]
})
)
config.cache = false;
return config
},
webpack5: true
}
module.exports = nextConfig
- add script tag in _app.tsx file of consumer app as follows:
import { AppProps } from "next/app";
import "bootstrap/dist/css/bootstrap.css";
import "../styles/globals.scss";
import Layout from "../components/layout";
import { persistor, store } from "../store/store";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import Authentication from "../config/auth.gaurd";
import Head from "next/head";
import React from "react";
import Script from "next/script";
function MyApp({ Component, pageProps }: AppProps) {
return (
<Layout>
<>
<Script src="http://localhost:3000/jobs-assets/_next/static/chunks/pages/cm_insurance_web.js" />
<Head>
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@100;200;300;400;500;600;700&display=swap"/>
<link rel="shortcut icon" href="/favicon2.ico"/>
<title>Jobboard Search</title>
</Head>
<Script id="gtm-script" strategy="afterInteractive">
{`(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-TKJH8RR');`
}
</Script>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Authentication>
<noscript dangerouslySetInnerHTML={{ __html:
`<iframe src="https://www.googletagmanager.com/ns.html?id=GTM-TKJH8RR"
height="0" width="0" style="display:none;visibility:hidden"></iframe>
`}}>
</noscript>
<Component {...pageProps} />
</Authentication>
</PersistGate>
</Provider>
</>
</Layout>
);
}
export default MyApp;
- Let’s import that module into the index.tsx file and use it.
import {NextPage} from "next";
import React, {lazy, Suspense, useState} from "react";
import dynamic from 'next/dynamic'
const InsuranceDetail2 = dynamic(() => import(('InsuranceA/InsuranceDetail')), {
ssr: false
}) as NextPage;
const Insurance: NextPage = ({}: any) => {
return (
<InsuranceDetail2 />
)
}
export default Insurance
After doing above steps I was able to see that remote js file is being loaded in browser's network tab but remote component is not getting rendered and getting blank page.
Please find attached screenshot
Please let me know if I am missing anything here. I took the reference from below links.