Skip to main content
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

name
string
required
Unique name for this container
filename
string
Filename of the container entry
exposes
object
Modules that should be exposed from this container
remotes
object
Containers to consume as remote
shared
array | object
Modules that should be shared between containers
library
object
Library configuration for the container
runtime
string | false
Custom runtime chunk name
shareScope
string
default:"default"
Name of the share scope

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

singleton
boolean
default:"false"
Only a single version of the shared module is allowed
strictVersion
boolean
default:"false"
Reject shared module if version is not valid
requiredVersion
string | false
Required version of shared module
eager
boolean
default:"false"
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.