This repo will serve as an example for how to configure a host application and a remote application using Webpack 5's Module Federation.
- Clone the repo
- Run
npm install
- Run
npm run init
to install the dependencies for the host & remote applications - Run
npm run start
which will build thedist
directory of each application and run both the host and the remote app on port8080
and8081
respectively
In order to utilize module federation, both the host and remote applications must be running the following, in addition it is strongly recommended although not required that both applications are using React.
- Node.js > 19.5.x
- Webpack > 5.x.x
The configuration for both the host and remote applications can be found in the webpack.config.js
file of the respective directory.
Ports can be modified by updating the serve
script in the package.json
file.
Note: The index.jsx
file in the src
directory of the host/remote application is only used to load the App
component. This is done a little differently from the standard index.jsx
because of the way Module Federation works. More info
The package.json
which is in the root directory uses a package called concurrently
to run both the host and remote applications. If you're having issues with the npm run start
command, you can run the host and remote applications separately.
If needed, you can run npm run clean
to remove the dist
directory from both the host and remote applications.
This is a top-level application which will host the remote application(s).
Runs on port 8080
You'll need to specify which remote applications you want to load in the webpack.config.js
file. Depending on whether you're running the remote application locally or on a remote server, you'll need to update the remotes
object in the webpack.config.js
file.
plugins: [
new ModuleFederationPlugin({
name: "host_app",
filename: "remoteEntry.js",
remotes: {
remote_app:
"remote_app@https://cwsites.github.io/module-federation-remote/remoteEntry.js",
},
exposes: {},
shared: {
react: {
requiredVersion: false,
singleton: true,
},
},
}),
],
remote_app: "remote_app@https://cwsites.github.io/module-federation-remote/remoteEntry.js";
remote_app: "remote_app@http://localhost:8081/remoteEntry.js";
There are multiple ways to import components from the remote application.
// import HelloWorld as a default component
const HelloWorld = React.lazy(() => import("remote_app/HelloWorld"));
// import NonDefaultHelloWorld as a non-default component
const NonDefaultHelloWorld = React.lazy(() => import("remote_app/HelloWorld").then(module => ({ default: module.NonDefaultHelloWorld })));
...
<>
<Suspense fallback={"loading..."}>
<HelloWorld />
</Suspense>
</>
import DefaultComponent, { NotDefaultComponent } from "remote_app/Components";
...
<>
<DefaultComponent />
<NotDefaultComponent />
</>
This is the most flexible way to load remote components. It allows you to specify the URL, scope, and module of the remote application.
Sample URL when running host app locally
You can either do this by passing in the URL, scope, and module as query parameters in the URL or by setting them in the host.
import { Suspense, useEffect, useState } from "react";
import { ModuleLoader } from "./dynamic-loader/ModuleLoader";
const App = () => {
const [remote, setRemote] = useState(null);
useEffect(() => {
const url =
"https://cwsites.github.io/module-federation-remote/remoteEntry.js";
const scope = "demo_remote_app";
const module = "./HelloUniverse";
setRemote({ url, scope, module });
}, []);
return (
<Suspense fallback={"loading dynamic Hello World..."}>
{remote && (
<ModuleLoader
url={remote.url}
scope={remote.scope}
module={remote.module}
/>
)}
</Suspense>
);
};
To prevent the entire application from crashing when a remote module fails to load, proper error handling is required. This can be done by wrapping the remote component in a ErrorBoundary
component and providing a fallback component.
import { ErrorBoundary } from 'react-error-boundary'
...
const ErrorFallback = () => {
return <h3>Something went wrong</h3>;
};
const App = () => {
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<HelloWorld />
</ErrorBoundary>
);
};
This is the application which will be loaded by the host application.
Runs on port 8081
You'll need to give your remote application a unique name in the webpack.config.js
file. This name will be used to reference the remote application in the host application. You can export more than one component from any file, however only one default component can be exported per file.
plugins: [
new ModuleFederationPlugin({
name: "remote_app",
filename: "RemoteEntry.js",
exposes: {
"./HelloWorld": "./src/components/HelloWorld",
"./Components": "./src/components/Components",
},
}),
],
import React from "react";
const DefaultComponent = () => {
return <h2>I am the default component</h2>;
};
export const NotDefaultComponent = () => {
return <h3>I am not a default component</h3>;
};
export default DefaultComponent;