Shared Dependency Isolation: Multiple Share Scopes
Split
shareddependencies into multiple named shared pools (for exampledefault,scope1). Dependencies are only reused within the same pool.
Background
In Module Federation, shared dependencies are registered into the default share scope by default. A single scope is often not enough when:
- You want to isolate part of your shared dependencies from the default pool (for example, running two React ecosystems side-by-side, gradual upgrades, or domain isolation in micro-frontends).
- You want the same package to use different versions or strategies in different domains, while still being shared within each domain (singleton/reuse still works within a domain).
The key idea of multiple share scopes is: move shared dependency registration and resolution into different namespaces (scopes), so you can isolate shared pools and layer policies.
Configuration Quick Map
The easiest mental model is to focus on what you configure on producer, consumer, and each shared entry:
- Producer (remote): use shareScope to declare which share scopes this remote initializes (default:
default, supportsstring | string[]). - Consumer (host): use remotes[remote].shareScope to declare which share scopes the host aligns with that remote (default:
default). - Shared entry: use
shared[pkg].shareScopeto decide which pool a dependency is registered/resolved in (see shared.shareScope).
What Happens for Different Combinations
When the host initializes a remote, it first aligns the share scopes based on both sides’ shareScope settings (so the remote knows which shared pools can reuse the host’s), then the remote initializes shared dependencies according to its own shareScope.
Below, HostShareScope refers to remotes[remote].shareScope on the host, and RemoteShareScope refers to shareScope on the remote.
Notes:
- If a scope does not exist on the host, the host will treat it as
{}for this initialization, so it won’t crash due to missing scope names. - With multiple share scopes, the remote will try to initialize all scopes listed in RemoteShareScope.
Build Plugin Configuration
You typically configure share scopes at three levels:
- Remote (producer): which scopes the remote initializes (shareScope).
- Host (consumer): which scopes the host aligns with that remote (remotes[remote].shareScope).
- Shared entry: which scope a dependency belongs to (
shared[pkg].shareScope, see shared.shareScope).
Producer
Key points:
shareScope: ['default','scope1']controls which pools the remote initializes at runtime.shared[pkg].shareScopedecides which pool a dependency participates in. If@company/design-systemis inscope1, it only participates in version selection and reuse within thescope1pool.
Consumer
Key points:
remotes[remote].shareScopecontrols which pools the host aligns when initializing that remote.- If the host uses multiple scopes but the remote uses a single scope, the pools are aligned but the remote only initializes shared deps for its single scope. For multi-scope reuse to “really work”, the host and remote usually need to agree on the same scopes.
Pure Runtime (Runtime API)
If you don’t rely on build-time remotes (or you want to register remotes/shared dynamically at runtime), you can configure the same idea via runtime API:
- Multi-scope remotes: use
registerRemotes/createInstance({ remotes })and setshareScope: string | string[]in the remote config. - Shared entry scope: use
registerShared/createInstance({ shared })and setscope: string | string[]in the shared config (note the field name isscopehere).
Fine-grained Control with Runtime Hooks
Multiple share scopes essentially group shared pools by name. If you need finer control over scope selection, alignment, and fallback strategies, you can use runtime hooks (runtime plugins) to intervene during remote initialization or shared resolution.
1) Force a remote to use a specific scope (beforeInitContainer)
The example below forces legacy_remote to always use the legacy scope:
2) Fallback / alias when a scope is missing (initContainerShareScopeMap / resolveShare)
initContainerShareScopeMap: adjust the share pool mapping during remote initialization.resolveShare: intervene when selecting a specific shared version, useful for “fallback to default if not found in current scope”.
Example: if a package is not found in scope1, fall back to default:
You can also alias one scope to another (so they share the same pool object):