In a Module Federation setup, what does marking a shared dependency like react as singleton: true with a requiredVersion do, and what happens if a host and remote have incompatible versions?
answer
- singleton = one instance only
- requiredVersion = semver range per app
- strictVersion: true throws on mismatch
- shared scope = runtime version registry
- duplicate React = broken hooks/context
basics
~20 sShared deps are libraries (like React) that separately-built apps agree to reuse one copy of instead of each shipping their own. Marking one 'singleton' means only one copy is ever allowed to run at once; if the versions genuinely clash, the app can crash or silently duplicate the library, either way it's risky.
solid answer
~50 sshared config lets Webpack dedupe libraries across independently built host/remote bundles instead of each shipping its own copy. singleton: true enforces that at most one instance of that package is active in the page at once — important for libraries like React that keep module-level state and break with duplicated instances. At init time, each federated app registers its available version and requiredVersion range into a runtime shared scope; Webpack resolves one mutually acceptable version per package and hands every consumer the same live module. If host and remote declare incompatible required versions for a singleton, with strictVersion: true the runtime throws rather than silently violating the singleton guarantee; without it, the requester falls back to its own bundled copy, which restores correctness for that one app but breaks the singleton assumption and can cause hard-to-diagnose divergent state elsewhere.
go deeper
Should understand at a high level that sharing avoids loading React twice, and that 'singleton' means only one copy is allowed.
Should be able to configure shared with singleton and requiredVersion correctly and explain what problem it solves.
Should walk through the runtime negotiation mechanism via init and the shared scope, explain the difference between strictVersion: true and the fallback behavior, and reason about which failure mode is worse in a given scenario.
Should discuss this as an org-design problem — how a company governs cross-team compatibility for shared singleton packages, how to plan major-version migrations across many independently deployed remotes, and when to deliberately not share a dependency to avoid coupling teams' release cadences.
## What singleton actually promises The `shared` option in `ModuleFederationPlugin` tells Webpack which libraries a host and its remotes should attempt to reuse a single instance of at runtime instead of each bundle shipping and loading its own private copy. Marking an entry like `react` with `singleton: true` is a strict instruction: at most one instance of that library may ever be active in the shared scope at a time, no matter how many federated apps request it. This matters because libraries like React and React DOM keep module-level mutable state (the fiber tree, hook dispatcher, context registries) that breaks in confusing ways if two separate copies end up mounted in the same page even though each one is individually valid code, such as - 'Invalid hook call' errors, - context providers that don't reach consumers, - or duplicated event listeners. ## How version negotiation works The mechanism behind this is version negotiation through what Webpack calls the **shared scope**, effectively a runtime registry keyed by package name and semver range. 1. When a federated app initializes, its container's `init(shareScope)` runs, it registers every entry from its own `shared` config into that scope, announcing 'I have [email protected] and I need a version satisfying ^18.0.0.' 2. As each app in the composition does this, Webpack's runtime resolves, for each shared package name, which single concrete version satisfies the most requesters and actually loads only that one module graph, handing out the same live module object to every consumer that asked for a compatible range. `requiredVersion` is what defines the acceptable semver range for a given app's request, defaulting to whatever's in its own package.json if omitted, and `strictVersion` controls whether an unsatisfiable range throws at runtime or falls back to a bundled-in copy. ## When the versions genuinely clash When a host and remote declare genuinely incompatible required versions for a singleton — say the host is pinned to react@^17 and a newly deployed remote requires react@^18 — Webpack's runtime has to pick one behavior. | Setting | What the runtime does | The reasoning | |---|---|---| | `strictVersion: true`, common for singletons | throws a runtime error during initialization rather than silently loading two React copies | for a `singleton: true` package silently violating the singleton guarantee is considered worse than failing loudly | | `strictVersion: false`, or when singleton isn't set | lets the requester fall back to a self-contained copy bundled into that app | restoring correctness for that one app at the cost of shipping duplicate library weight and losing the 'single instance' guarantee its own config had implied | For something like a router or global state library this can produce very hard-to-diagnose bugs, because the app runs without error but two independent module instances silently diverge in state. ## Why the system exists at all This system exists because Module Federation's whole value proposition — independently deployed apps composing into one page as if they were one app — collapses if every remote drags in its own multi-hundred-kilobyte copy of the UI framework: bundle size balloons, and framework-level singleton state breaks across the composed page. Sharing gives back both the bundle-size win of a monorepo and, when versions line up, framework-level continuity across independently built apps, at the cost of a genuinely hard operational problem: every team touching a shared dependency's major version now has an implicit cross-team compatibility contract, even though nothing in their separate CI pipelines enforces it before deploy. ## How teams manage it in practice In practice, teams manage this by: - **treating shared, singleton dependencies** as a slow-moving, centrally governed layer — often pinning exact ranges for React, React DOM, and a shared router across every remote and coordinating major version bumps as a cross-team migration rather than a per-app decision, - while **leaving smaller, less state-sensitive libraries** either un-shared, each app bundles its own, or shared but non-singleton, where duplication is a bundle-size cost rather than a correctness risk. A concrete example: a large B2B console with a shell and eight remotes shares react, react-dom, and their design-system package as strict singletons pinned to one version, but leaves date-formatting or charting libraries un-shared, accepting the extra kilobytes so any single team can upgrade its own chart library without a fleet-wide coordination effort.
- Why is duplicating React specifically so much worse than duplicating a stateless utility library?React and React DOM hold module-level mutable state — the current fiber tree, the hook dispatcher, context registries. Two independently loaded copies each think they're the only instance managing the page, which is exactly what triggers errors like 'Invalid hook call' or context providers that don't reach their consumers, even though each copy is individually valid code.
- What's the practical difference between not sharing a library at all versus sharing it but not as a singleton?Not sharing means every app always bundles and loads its own private copy — simple, but pure bundle-size waste for common libraries. Sharing-without-singleton lets Webpack still deduplicate when versions happen to be compatible, falling back to per-app copies only when they aren't, so it's a strict improvement over not sharing for stateless or side-effect-free libraries.
- How do teams avoid runtime singleton version-conflict errors in practice?Most treat shared, singleton dependencies as a centrally governed layer — pinning exact or narrow ranges for framework-level packages across every remote and coordinating major version bumps as a deliberate cross-team migration, rather than letting each team's independent deploy silently drift the required range.
It's like several departments in a building agreeing to share one printer instead of each buying its own — fine as long as everyone's driver software is compatible; if one department's driver absolutely requires a firmware version the shared printer can't run, that department either has to refuse the shared printer (error) or quietly wheel in its own private printer (fallback), which then behaves differently from what everyone else expects.
saying these in an interview costs you the question
- thinks 'shared' guarantees deduplication regardless of version compatibility
- doesn't know singleton violations can throw at runtime rather than at build time
- assumes falling back to a bundled copy is always safe
- can't explain why React specifically needs this more than a generic utility library
- thinks version negotiation happens at build time instead of runtime init