🤝 Webpack Module Federation
Module Federation is one of the most exciting features in Webpack 5 and is considered a game-changer in JavaScript architecture. It supports more independent and straightforward code sharing at runtime among JavaScript applications, making the applications more adaptive and dynamic.
What is Module Federation?
Module Federation is a technique that enables code to be split into smaller, deployable modules that can be shared and consumed at runtime between applications. This approach supports the development of micro frontends, reducing the need for tight coordination between teams and allowing each team to release on its own schedule.
While teams can release independently, some changes—such as package upgrades—should still be coordinated. Uncoordinated upgrades can lead to incompatibility and unexpected behavior between applications in a Module Federation architecture. For example, upgrading Angular or React should be coordinated to avoid issues caused by loading different versions at runtime.
Module Federation introduces three key terms for the applications involved: host, remote, and federated modules.
What is a Host?
A host is an application that consumes federated modules from remote applications at runtime. When developing a host, you import modules from remotes as if they were part of your build. At build time, Webpack knows these modules will only be available at runtime, after fetching the JS bundle from the remote application.
plugins: [
new container.ModuleFederationPlugin({
remotes: {
app: 'app@/public/remoteEntry.js',
},
}),
]
The host is responsible for defining which remotes it will consume and how those modules are loaded. Hosts can dynamically load remote modules, enabling features like lazy loading and on-demand updates.
What is a Remote?
A remote is an application that exposes federated modules, making them available to hosts over the network at runtime. These modules can be anything from React components to Angular routing files, plain JavaScript objects, and more.
plugins: [
new container.ModuleFederationPlugin({
filename: 'remoteEntry.js',
name: 'app',
exposes: {
'./Button': './src/components/Button', // Example: expose a Button component
},
shared: {
react: { singleton: true, eager: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, eager: true, requiredVersion: '^18.0.0' },
},
}),
]
filename
: The entry file that will be loaded by hosts.name
: The global variable name for the remote.exposes
: The modules/components to expose.shared
: Shared dependencies to avoid duplication and version conflicts.
What is a Federated Module?
A federated module is any JavaScript module exposed by a remote application, intended to be consumed by a host. This can include UI components, services, application state, functions, and more—enabling code sharing and updates without redeploying everything.
Explore Module Federation examples here.
How Module Federation Works
- At build time, Webpack generates a manifest (
remoteEntry.js
) for each remote, describing what modules are available. - At runtime, the host loads the remote's manifest and fetches the required modules over HTTP.
- Shared dependencies (like React) are managed to avoid duplication and version conflicts.
- Modules can be loaded eagerly or lazily, supporting advanced use cases like A/B testing, plugin systems, and dynamic feature delivery.
Pros of Module Federation
Reduced Code Duplication
Module Federation allows you to share modules between applications, significantly reducing code duplication and the size of each application.
Greater Flexibility
By breaking applications into smaller, manageable modules, you can swap or update modules independently, increasing flexibility and modularity.
Improved Scalability
Distributing functionality across multiple applications makes each one easier to maintain and scale.
Independent Deployment
Teams can deploy and update their modules independently, enabling faster release cycles and reducing bottlenecks.
Dynamic Loading
Modules can be loaded on demand, reducing initial bundle size and improving performance.
Common Pitfalls
Module Federation introduces complexity, especially with independent deployments and different release cadences.
Increased Bundle Size
Sharing third-party packages (like react
or react-dom
) across remotes and hosts can prevent redundant downloads, but limits Webpack's ability to tree-shake unused code, potentially increasing bundle size. To mitigate this, only share what is necessary.
Managing Versions
Managing compatible versions of shared packages is crucial. If a remote uses a different version than the host, multiple versions may be loaded, breaking singleton patterns and causing unexpected behavior. Use the singleton
and requiredVersion
options in the shared
config to enforce version consistency.
Security Risks
Sharing code between applications can introduce vulnerabilities if proper security measures are not in place. Always validate and sanitize data exchanged between hosts and remotes, and avoid exposing sensitive logic.
Increased Coupling
Module Federation can increase coupling between applications, making changes more challenging and potentially reducing flexibility. Design clear contracts and interfaces for shared modules.
Local Development Complexity
Running multiple apps locally (host and remotes) can be challenging. Use tools like Webpack Dev Server and consider using dynamic remote URLs for easier development.
Use Cases for Module Federation
Micro-frontend Architecture
Break large monolithic apps into smaller, independently developed and deployed front-end modules. Module Federation enables seamless sharing and integration of these modules.
Multi-Application Integration
Integrate multiple applications that share common functionality by sharing modules, enabling seamless collaboration and data sharing.
Shared Libraries
Share libraries and common code between applications to reduce duplication and improve collaboration.
Plugin Systems
Build extensible applications where plugins can be loaded at runtime as federated modules, enabling third-party or internal teams to extend functionality without redeploying the core app.
Best Practices
- Version Control: Use strict versioning and
singleton
for shared dependencies to avoid conflicts. - Limit Shared Scope: Only share what is necessary; avoid sharing large libraries unless required.
- Testing: Test integration between host and remotes regularly, especially after dependency upgrades.
- Documentation: Document exposed modules and their interfaces clearly for consumers.
- Fallbacks: Provide fallback UIs or error handling if a remote is unavailable at runtime.
- Security: Avoid exposing sensitive logic or data via federated modules.
Conclusion
Module Federation is a powerful tool for reducing duplication, improving collaboration, and increasing flexibility in JavaScript application design. By evaluating your application's needs, you can determine if Module Federation is the right solution—whether for micro-frontends, multi-app integration, or shared libraries.
For more details, see the official Webpack Module Federation documentation and awesome examples.