The ModuleFederationPlugin enables multiple separate builds to form a single application. These separate builds act like containers and can expose and consume code between builds.
Usage
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'app1',
filename: 'remoteEntry.js',
exposes: {
'./Component': './src/Component'
},
shared: ['react', 'react-dom']
})
]
};
Configuration
Unique name for this container
Filename of the container entry
Modules that should be exposed from this container
Containers to consume as remote
Modules that should be shared between containers
Library configuration for the container
Custom runtime chunk name
Examples
Exposing Modules (Host)
new ModuleFederationPlugin({
name: 'host',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button',
'./Header': './src/components/Header'
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true }
}
})
Consuming Remote Modules
new ModuleFederationPlugin({
name: 'consumer',
remotes: {
host: 'host@http://localhost:3001/remoteEntry.js'
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true }
}
})
Then use the remote module:
import Button from 'host/Button';
function App() {
return <Button>Click me</Button>;
}
Bidirectional Hosts
// App 1
new ModuleFederationPlugin({
name: 'app1',
filename: 'remoteEntry.js',
exposes: {
'./ComponentA': './src/ComponentA'
},
remotes: {
app2: 'app2@http://localhost:3002/remoteEntry.js'
},
shared: ['react', 'react-dom']
})
// App 2
new ModuleFederationPlugin({
name: 'app2',
filename: 'remoteEntry.js',
exposes: {
'./ComponentB': './src/ComponentB'
},
remotes: {
app1: 'app1@http://localhost:3001/remoteEntry.js'
},
shared: ['react', 'react-dom']
})
Advanced Sharing
new ModuleFederationPlugin({
name: 'app',
shared: {
react: {
singleton: true,
requiredVersion: '^18.0.0',
eager: false
},
'react-dom': {
singleton: true,
requiredVersion: '^18.0.0'
},
lodash: {
requiredVersion: false
}
}
})
Shared Module Options
Only a single version of the shared module is allowed
Reject shared module if version is not valid
Required version of shared module
Include shared module in initial chunk instead of async
Dynamic Remote Containers
const loadComponent = (scope, module) => {
return async () => {
await __webpack_init_sharing__('default');
const container = window[scope];
await container.init(__webpack_share_scopes__.default);
const factory = await container.get(module);
return factory();
};
};
loadComponent('host', './Button')().then(module => {
// Use the module
});
Use Cases
Micro-Frontends
Share components between different applications:
// Shell app
new ModuleFederationPlugin({
name: 'shell',
remotes: {
dashboard: 'dashboard@http://localhost:3001/remoteEntry.js',
profile: 'profile@http://localhost:3002/remoteEntry.js'
}
})
Library Sharing
Reduce bundle size by sharing common libraries:
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
'react-router-dom': { singleton: true }
}
Module Federation works best when combined with proper versioning and deployment strategies.
Be cautious with singleton shared modules. Ensure all federated modules are compatible with the shared version.