# webpack.config.js
```javascript
const path = require("path");
const { ModuleFederationPlugin } = require("../../").container;
const rules = [
{
test: /\.js$/,
include: path.resolve(__dirname, "src"),
use: {
loader: "babel-loader",
options: {
presets: ["@babel/react"]
}
}
}
];
const optimization = {
chunkIds: "named", // for this example only: readable filenames in production too
nodeEnv: "production" // for this example only: always production version of react
};
const stats = {
chunks: true,
modules: false,
chunkModules: true,
chunkOrigins: true
};
module.exports = (env = "development") => [
// For this example we have 3 configs in a single file
// In practice you probably would have separate config
// maybe even separate repos for each build.
// For Module Federation there is not compile-time dependency
// between the builds.
// Each one can have different config options.
{
name: "app",
mode: env,
entry: {
app: "./src/index.js"
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist/aaa"),
publicPath: "dist/aaa/",
// Each build needs a unique name
// to avoid runtime collisions
// The default uses "name" from package.json
uniqueName: "module-federation-aaa"
},
module: { rules },
optimization,
plugins: [
new ModuleFederationPlugin({
// List of remotes with URLs
remotes: {
"mfe-b": "mfeBBB@/dist/bbb/mfeBBB.js",
"mfe-c": "mfeCCC@/dist/ccc/mfeCCC.js"
},
// list of shared modules with optional options
shared: {
// specifying a module request as shared module
// will provide all used modules matching this name (version from package.json)
// and consume shared modules in the version specified in dependencies from package.json
// (or in dev/peer/optionalDependencies)
// So it use the highest available version of this package matching the version requirement
// from package.json, while providing it's own version to others.
react: {
singleton: true // make sure only a single react module is used
}
}
})
],
stats
},
{
name: "mfe-b",
mode: env,
entry: {},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist/bbb"),
publicPath: "dist/bbb/",
uniqueName: "module-federation-bbb"
},
module: { rules },
optimization,
plugins: [
new ModuleFederationPlugin({
// A unique name
name: "mfeBBB",
// List of exposed modules
exposes: {
"./Component": "./src-b/Component"
},
// list of shared modules
shared: [
// date-fns is shared with the other remote, app doesn't know about that
"date-fns",
{
react: {
singleton: true // must be specified in each config
}
}
]
})
],
stats
},
{
name: "mfe-c",
mode: env,
entry: {},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist/ccc"),
publicPath: "dist/ccc/",
uniqueName: "module-federation-ccc"
},
module: { rules },
optimization,
plugins: [
new ModuleFederationPlugin({
name: "mfeCCC",
exposes: {
"./Component": "./src-c/Component",
"./Component2": "./src-c/LazyComponent"
},
shared: [
// All (used) requests within lodash are shared.
"lodash/",
"date-fns",
{
react: {
// Do not load our own version.
// There must be a valid shared module available at runtime.
// This improves build time as this module doesn't need to be compiled,
// but it opts-out of possible fallbacks and runtime version upgrade.
import: false,
singleton: true
}
}
]
})
],
stats
}
];
```
# src/index.js
```javascript
// Sharing modules requires that all remotes are initialized
// and can provide shared modules to the common scope
// As this is an async operation we need an async boundary (import())
// Using modules from remotes is also an async operation
// as chunks need to be loaded for the code of the remote module
// This also requires an async boundary (import())
// At this point shared modules initialized and remote modules are loaded
import("./bootstrap");
// It's possible to place more code here to do stuff on page init
// but it can't use any of the shared modules or remote modules.
```
# src/bootstrap.js
```jsx
import ReactDom from "react-dom";
import React from "react"; // <- this is a shared module, but used as usual
import App from "./App";
// load app
const el = document.createElement("main");
ReactDom.render(<App />, el);
document.body.appendChild(el);
// remove spinner
document.body.removeChild(document.getElementsByClassName("spinner")[0]);
```
# src/App.js
```jsx
import React from "react";
import ComponentB from "mfe-b/Component"; // <- these are remote modules,
import ComponentC from "mfe-c/Component"; // <- but they are used as usual packages
import { de } from "date-fns/locale";
// remote modules can also be used with import() which lazy loads them as usual
const ComponentD = React.lazy(() => import("mfe-c/Component2"));
const App = () => (
<article>
<header>
<h1>Hello World</h1>
</header>
<p>This component is from a remote container:</p>
<ComponentB locale={de} />
<p>And this component is from another remote container:</p>
<ComponentC locale={de} />
<React.Suspense fallback={<p>Lazy loading component...</p>}>
<p>
And this component is from this remote container too, but lazy loaded:
</p>
<ComponentD />
</React.Suspense>
</article>
);
export default App;
```
# index.html
```html
<html>
<head>
<style>
.spinner {
font-size: 10px;
margin: 50px auto;
text-indent: -9999em;
width: 11em;
height: 11em;
border-radius: 50%;
background: #595959;
background: linear-gradient(
to right,
#595959 10%,
rgba(89, 89, 89, 0) 42%
);
position: relative;
animation: spin 1.4s infinite linear;
transform: translateZ(0);
}
.spinner:before {
width: 50%;
height: 50%;
background: #595959;
border-radius: 100% 0 0 0;
position: absolute;
top: 0;
left: 0;
content: "";
}
.spinner:after {
background: white;
width: 75%;
height: 75%;
border-radius: 50%;
content: "";
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<!-- A spinner -->
<div class="spinner"></div>
<!-- This script only contains bootstrapping logic -->
<!-- It will load all other scripts if necessary -->
<script src="/dist/aaa/app.js" async></script>
<!-- These script tags are optional -->
<!-- They improve loading performance -->
<!-- Omitting them will add an additional round trip -->
<script src="/dist/bbb/mfeBBB.js" async></script>
<script src="/dist/ccc/mfeCCC.js" async></script>
<!-- All these scripts are pretty small ~5kb -->
<!-- For optimal performance they can be inlined -->
</body>
</html>
```
# src-b/Component.js
```jsx
import React from "react";
import { formatRelative, subDays } from "date-fns";
// date-fns is a shared module, but used as usual
// exposing modules act as async boundary,
// so no additional async boundary need to be added here
// As data-fns is an shared module, it will be placed in a separate file
// It will be loaded in parallel to the code of this module
const Component = ({ locale }) => (
<div style={{ border: "5px solid darkblue" }}>
<p>I'm a Component exposed from container B!</p>
<p>
Using date-fn in Remote:{" "}
{formatR