Skip to content

Shared Dependencies

When multiple micro frontends are composed into a single page, each one may bundle its own copy of common libraries — React, lodash, a design system. Left unchecked, this leads to bloated bundles, longer load times, and runtime conflicts (e.g., two versions of React fighting over the DOM). Managing shared dependencies is one of the most critical challenges in micro frontend architecture.

Consider three micro frontends on the same page, each bundling React 18 independently:

Micro FrontendReact BundleOther DepsTotal
Product42 KB85 KB127 KB
Cart42 KB60 KB102 KB
Checkout42 KB120 KB162 KB
Page Total126 KB265 KB391 KB

The user downloads React three times. With shared dependency management, React is loaded once (42 KB), reducing the page total to 307 KB — a 21% reduction from a single optimization.

Mark shared libraries as externals in your bundler config so they are not included in each micro frontend’s bundle. Instead, the shell or a shared CDN provides them.

// webpack.config.js for a micro frontend
module.exports = {
externals: {
react: 'React',
'react-dom': 'ReactDOM',
},
};

The shell loads them via <script> tags:

<script src="https://cdn.example.com/react@18/umd/react.production.min.js"></script>
<script src="https://cdn.example.com/react-dom@18/umd/react-dom.production.min.js"></script>

Pros: Simple, well-understood, works with any bundler. Cons: Requires coordination on versions, uses global scope, limited to UMD builds.

A browser-native mechanism for controlling module resolution. Import maps let you redirect bare specifiers to specific URLs without a bundler.

<script type="importmap">
{
"imports": {
"react": "https://esm.sh/react@18.3.1",
"react-dom": "https://esm.sh/react-dom@18.3.1",
"design-system": "https://cdn.example.com/ds@4.2.0/index.js"
}
}
</script>

Each micro frontend simply imports react and the browser resolves it to the shared URL.

Pros: No bundler plugins required, standards-based, per-page override capability. Cons: Browser support (polyfills needed for older browsers), debugging can be tricky.

Webpack Module Federation provides a shared configuration that handles version negotiation automatically at runtime.

new ModuleFederationPlugin({
name: 'product',
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
},
});

When multiple micro frontends declare the same shared dependency, Module Federation loads the highest compatible version once and shares it across all consumers.

Pros: Automatic version negotiation, no external script tags, works with the module system. Cons: Tied to Webpack/Rspack ecosystem, runtime overhead for negotiation.

See the Module Federation page for a deeper dive.

A centralized host application provides common libraries as part of its own bundle, and micro frontends import from the host.

Pros: Full control over versions, single source of truth. Cons: Tighter coupling to the host, less autonomy for micro frontend teams.

Some libraries must exist as a single instance on the page. React is the canonical example — two copies of React cause hooks to break because they maintain separate internal state.

Singleton sharing ensures that regardless of how many micro frontends request a library, only one instance is loaded and shared across all.

With Module Federation:

shared: {
react: {
singleton: true, // only one copy allowed
strictVersion: true, // fail if versions are incompatible
requiredVersion: '^18.0.0',
},
}

Without Module Federation, the externals or import map approach naturally enforces singletons since there is only one script loaded.

The hardest part of shared dependencies is version alignment across independently deployed micro frontends.

All teams agree on exact versions (e.g., react@18.3.1). Simple but rigid — upgrading requires coordinating all teams simultaneously.

Teams specify compatible ranges (e.g., ^18.0.0). The runtime loads the highest available version within the range. Module Federation supports this natively.

When versions are truly incompatible, a micro frontend can fall back to its own bundled copy. This sacrifices some performance for resilience:

shared: {
react: {
singleton: true,
requiredVersion: '^18.0.0',
strictVersion: false, // allow fallback to bundled version
},
}
More SharingLess Sharing
Bundle sizeSmaller (deduplicated)Larger (duplicated)
CouplingHigher (shared version constraints)Lower (full independence)
AutonomyReduced (teams must coordinate)Maximized (teams choose freely)
RiskShared failure (bad version affects all)Isolated failure

The right balance depends on your organization’s priorities. Most teams start by sharing framework-level dependencies (React, Vue, Angular) and a design system, while keeping application-specific libraries isolated.

For related strategies, see performance optimization techniques and versioning patterns across deployments.

Go Deeper in the Book

This topic is covered in depth in Chapter 4 of Micro Frontends Architecture for Scalable Applications, with detailed examples, diagrams, and production-ready patterns.