Module federation brings dynamic code composition to JavaScript on both client and server
The equivalent shift for bundlers that Apollo delivered for GraphQL
Scalable code sharing across independent applications has never been effortless, and at enterprise scale it has been nearly unworkable. The closest alternatives—externals or the DLLPlugin—forced everything to rely on a centrally managed external file. This made sharing awkward, left applications short of truly independent, and in practice limited how many dependencies could be shared. And sharing actual feature code or UI components between separately bundled applications? That has been impractical, counterproductive, and unprofitable.
Prefer a shorter version of this material? Jack Herrington has put one together!
What we need is a scalable answer for sharing node modules and complete feature or application code. The sharing has to happen at runtime so it can stay responsive and dynamic. Externals is neither efficient nor flexible enough for this. Import maps don't solve problems of scale either. Simply fetching code and sharing a few dependencies isn't sufficient—we need an orchestration layer that can dynamically share modules at runtime, complete with fallbacks.
Some really exciting work going on here by @ScriptedAlchemy, aiming to make independently deployed webpack builds "look and feel like a monolith in the client."https://t.co/8rb4A5Ifyc
— Mark Dalgleish (@markdalgleish) February 18, 2020
What is Module Federation?
Module Federation is a JavaScript architecture I originally invented and built a prototype for. With the help of my co-creator and the founder of Webpack, it evolved into one of the most compelling features embedded in the Webpack 5 core (the release contains several strong additions, and the fresh API is notably powerful and tidy).
I'm proud to present, a long awaited leap forward in JavaScript application architecture. Our contribution to the open-source community:
Module Federation
Module Federation lets one JavaScript application pull in code from another application at runtime, and in that same process, share dependencies. When a consumer app that loads a federated module lacks a specific dependency required by that federated code, Webpack fetches the missing dependency directly from the build that provided the federated module.
Whenever possible, code gets shared, but each situation has its own fallback ready. Federated code is always able to load its own dependencies, though it will first try to use the consumer's existing dependencies before downloading any additional payload. The benefit is minimal code duplication and dependency sharing on par with a single monolithic Webpack build. While I initiated the system, it became part of Webpack 5 as a co-authored effort by myself (Zack Jackson) and Marais Rossouw, with extensive guidance, pair-programming, and support from Tobias Koppers. These developers were instrumental in rewriting and stabilizing Module Federation within the Webpack 5 core. Their ongoing collaboration and support have been invaluable.
Key Terminology
- Module federation: the same principle as Apollo GraphQL federation — but designed for JavaScript modules. It operates both in the browser and in node.js. We refer to it as Universal Module Federation.
- A host: the Webpack build that gets initialized first during a page load (when the onLoad event fires).
- A remote: a Webpack build that partially gets consumed by a “host”.
- Bidirectional-hosts: a bundle or Webpack build that can act as either a host or a remote. It can consume other applications or be consumed by them at runtime.

It should be stressed that this system is built so each completely standalone build/app can live in its own repository, be independently deployed, and run as its own independent SPA.
Every one of these applications is a bi-directional host. Whichever application happens to load first automatically becomes the host. As you navigate through an app and change routes, it loads federated modules just like it would with dynamic imports. But if you refresh the page, the application that starts up first on that specific load becomes the host.
This is killer stuff Zack, thanks! Playing with the demos now and I feel like this stuff can be a real game-changer. This is how I've wanted to compose apps in the past, but the burden it put on the consumer through bundle size / UX always felt painful. Hoping this nails it.
— Kevin Saldaña (@kmsaldana1) March 2, 2020
Imagine each page of a website is compiled and deployed separately. I want this micro-frontend style architecture, but I absolutely cannot afford page reloads when switching routes. I'd also like to dynamically share code & vendors between pages, making it just as efficient as a single large Webpack build using code splitting.
If you land on the home page app, the "home" page becomes the "host". Navigating to an "about" page means the host (the home page SPA) is dynamically importing a module from another independent application—specifically the about page SPA. It doesn't load the main entry point or spin up the entire application; it only pulls in a few kilobytes of code. If you're on the "about" page and refresh the browser, then the "about" page becomes the "host". Going back to the home page means the about page "host" is fetching a slice of runtime from a "remote"—this time, the home page. Every application is both a remote and a host; each is a consumer and is consumed by other federated modules across the system.
For deeper technical details, check the GitHub discussion: https://github.com/webpack/webpack/issues/10352
Constructing a federated application
Let's begin with three standalone applications.
App One
Settings:
I'll use the app container <App> from App One. Other apps will use this component. To allow this, I expose its App as AppContainer.
App One will also pull in components from two other federated applications. To enable that, I define the remotes scope:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
// other webpack configs...
plugins: [
new ModuleFederationPlugin({
name: "app_one_remote",
remotes: {
app_two: "app_two_remote",
app_three: "app_three_remote"
},
exposes: {
'AppContainer':'./src/App'
},
shared: ["react", "react-dom","react-router-dom"]
}),
new HtmlWebpackPlugin({
template: "./public/index.html",
chunks: ["main"]
})
]
}
Configuring build orchestration:
At the top of my applications, I include app_one_remote.js. This connects you to other Webpack runtimes and sets up the orchestration layer at runtime. It functions as a carefully crafted Webpack runtime and entry point. It is not a typical application entry point and is only a few KB in size.
Keep in mind these are special entry points—they are just a few KB. They contain a specialized Webpack runtime that can interact with the host, so they are NOT standard entry points
<head>
<script src="http://localhost:3002/app_one_remote.js"></script>
<script src="http://localhost:3003/app_two_remote.js"></script>
</head>
<body>
<div id="root"></div>
</body>
How to consume code from a remote
Within App One, there's a page that uses a dialog component originating from App Two.
const Dialog = React.lazy(() => import("app_two_remote/Dialog"));
const Page1 = () => {
return (
<div>
<h1>Page 1</h1>
<React.Suspense fallback="Loading Material UI Dialog...">
<Dialog />
</React.Suspense>
</div>
);
}
export default Page1;
The router itself looks quite standard:
import { Route, Switch } from "react-router-dom";
import Page1 from "./pages/page1";
import Page2 from "./pages/page2";
import React from "react";
const Routes = () => (
<Switch>
<Route path="/page1">
<Page1 />
</Route>
<Route path="/page2">
<Page2 />
</Route>
</Switch>
);
export default Routes;
App Two
Settings:
App Two will expose its Dialog component, which allows App One to use it. Additionally, App Two will use App One's <App> — so I define app_one as a remote, demonstrating bi-directional hosts:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "app_two_remote",
filename: "remoteEntry.js",
exposes: {
Dialog: "./src/Dialog"
},
remotes: {
app_one: "app_one_remote",
},
shared: ["react", "react-dom","react-router-dom"]
}),
new HtmlWebpackPlugin({
template: "./public/index.html",
chunks: ["main"]
})
]
};
How it consumes:
The root App component looks like this:
import React from "react";
import Routes from './Routes'
const AppContainer = React.lazy(() => import("app_one_remote/AppContainer"));
const App = () => {
return (
<div>
<React.Suspense fallback="Loading App Container from Host">
<AppContainer routes={Routes}/>
</React.Suspense>
</div>
);
}
export default App;
And the default page, which renders the Dialog, looks like this:
import React from 'react'
import {ThemeProvider} from "@material-ui/core";
import {theme} from "./theme";
import Dialog from "./Dialog";
function MainPage() {
return (
<ThemeProvider theme={theme}>
<div>
<h1>Material UI App</h1>
<Dialog />
</div>
</ThemeProvider>
);
}
export default MainPage
App Three
Predictably, App Three follows a similar pattern. The difference is that it does not consume the <App> from App One; it runs more like a standalone, self-sufficient component (no navigation or sidebar). Therefore, it does not define any remotes:
new ModuleFederationPlugin({
name: "app_three_remote",
library: { type: "var", name: "app_three_remote" },
filename: "remoteEntry.js",
exposes: {
Button: "./src/Button"
},
shared: ["react", "react-dom"]
}),
What you see in the browser
Keep an eye on the network tab. The code is being federated across three separate servers: three separate bundles. Generally speaking, the advice is to avoid federating the whole application container unless you plan to use SSR or progressive loading. Nevertheless, the concept is remarkably powerful.
A more robust demo of Module Federation. 3 Apps sharing dependencies and modules at runtime! Nested federation, circular federated imports, component importing, page importing, and nested routing. #microfrontends #webpack5 #modulefederation pic.twitter.com/7FbbioAxZ6
— Zack Jackson (@ScriptedAlchemy) March 2, 2020
Duplication of Code
Dependency duplication is nearly non-existent. Thanks to the shared option — remotes will rely on host dependencies; if the host doesn't have a given dependency, the remote will fetch its own copy. Thus, no code duplication, but built-in redundancy.
Module Federation across three separately deployed applications. Only 30kb of Javascript downloaded when moving between the applications. ?
— Zack Jackson (@ScriptedAlchemy) March 1, 2020
Manually adding vendors or other modules to the shared field isn't practical on a large scale. This process can be automated with a custom-built function or through a supplementary Webpack plugin. We do have plans to release AutomaticModuleFederationPlugin and will maintain it separately from the Webpack Core. Now that we have integrated first-class code federation directly into Webpack, extending its capabilities is a straightforward task.
But the big question is – Does any of this even work with SSR??
Server-Side Rendering
We have built this to be Universal. Module Federation operates in any environment. Federating code for server-side rendering is completely within reach. You just need your server builds to use a commonjs library target. There are multiple ways to get federated SSR running: through S3 Streaming, ESI, or automating an npm publish to consume server-side variants. My own plan involves a commonly shared file volume or async S3 streaming to shuttle files across the filesystem. This would enable the server to require federated code in the same manner as the browser, using fs rather than http to load the federated code.
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "container",
library: { type: "commonjs-module" },
filename: "container.js",
remotes: {
containerB: "../1-container-full/container.js"
},
shared: ["react"]
})
]
};
“Module Federation also works with
target: "node". In that case, you point to the other micro-frontends using file paths rather than URLs. This lets you do SSR with the same code base using a different webpack config for node.js builds. The properties of Module Federation remain true in node.js: e. g. Separate builds, Separate deploys” — Tobias Koppers
The Next.js Experiment with Webpack 5 Federation
Federation requires Webpack 5 — and Next.js doesn't officially support it yet. Even so, I managed to fork and update Next.js so it works with Webpack 5! There's still work to be done. Some middleware for development mode requires finishing touches. Production mode is functional, though I still need to retest some additional loaders.
Hello there beautiful. #nextjs upgraded and working with #webpack5 took a few hours but Module Federation with Next is going to be ?? pic.twitter.com/ZfTJ7mLjtO
— Zack Jackson (@ScriptedAlchemy) March 1, 2020
Let's talk, podcast, or get your feedback
I would love the chance to discuss this technology further. If you're planning to use Module Federation or a Federated architecture, we'd really like to hear about your experiences and any tweaks you've made to your current architecture. We are also eager to speak about it on Podcasts, at meetups, or in corporate settings. Reach me on Twitter: https://twitter.com/ScriptedAlchemy
You can also get in touch with my co-creator. Follow us for the latest updates on Module Federation, FOSA (Federation of Standalone Applications) Architecture, and other tools we are building—all designed to integrate with Federated Applications.
Module Federation in Action: Examples
The response from the community has been extremely positive! Both my co-creators and I have dedicated our efforts primarily to integrating this directly into Webpack 5. In the meantime, we hope some code samples are helpful while we work on polishing the remaining features and writing documentation: https://twitter.com/codervandal
[
Webpack 5 and Module Federation – A Microfrontend Revolution
Picture this: you’ve got yourself a pretty whiz-bang component, not just any component, but that clas…
The DEV CommunityMarais Rossouw

](https://dev.to/marais/webpack-5-and-module-federation-4j1i)
As bandwidth allows, we will create SSR examples and more comprehensive demos. If you're interested in building a project that could serve as a demo, we are happy to accept pull requests into webpack-external-import
[
module-federation/module-federation-examples
Examples showcasing Webpack 5′s Module Federation. Contribute to module-federation/module-federation-examples development by creating an account on GitHub.
GitHubmodule-federation

](https://github.com/module-federation/module-federation-examples)
[
module-federation/next-webpack-5
Contribute to module-federation/next-webpack-5 development by creating an account on GitHub.
GitHubmodule-federation

](https://github.com/module-federation/next-webpack-5)
[
ScriptedAlchemy/mfe-webpack-demo
Contribute to ScriptedAlchemy/mfe-webpack-demo development by creating an account on GitHub.
GitHubScriptedAlchemy
](https://github.com/ScriptedAlchemy/mfe-webpack-demo)
[
ScriptedAlchemy/webpack-external-import
Dynamically import modules from other webpack bundles. Painless code sharing between separate apps – ScriptedAlchemy/webpack-external-import
GitHubScriptedAlchemy
](https://github.com/ScriptedAlchemy/webpack-external-import)
