Featured image of post JavaScript Module Federation: Micro-Frontends in Practice Featured image of post JavaScript Module Federation: Micro-Frontends in Practice

JavaScript Module Federation: Micro-Frontends in Practice

Deep dive into Webpack 5 Module Federation for micro-frontends. Learn shared dependencies, remote containers, versioning, fallbacks, and deployment patterns.

Introduction

Module Federation, introduced in Webpack 5, provides a runtime integration mechanism for building micro-frontend architectures. Unlike build-time integration approaches, it enables independently deployed applications to share code at runtime, facilitating team autonomy and incremental migration without coordinated releases.

At its core, Module Federation allows a JavaScript application to dynamically load code from another application at runtime. Each participating application exposes a remoteEntry.js file that serves as the entry point. When a host application needs a component from a remote, it fetches the remoteEntry.js at runtime and resolves the required module on demand.

Configuration with ModuleFederationPlugin

The ModuleFederationPlugin is the primary configuration surface for defining how applications share code.

const { ModuleFederationPlugin } = require("webpack").container;

new ModuleFederationPlugin({
  name: "shell",
  remotes: {
    catalog: "catalog@https://cdn.example.com/catalog/remoteEntry.js",
  },
  exposes: {
    "./Header": "./src/components/Header",
  },
  shared: {
    react: { singleton: true, requiredVersion: "^18.0.0" },
  },
});

The name field serves as the application’s unique identifier. The exposes map declares which modules are made available to other applications, while remotes specifies the external applications consumed. The shared configuration ensures critical libraries like React are loaded only once, preventing duplicate instances in the browser.


The remoteEntry.js Runtime

When the browser loads the host application, it does not immediately fetch federated modules. Instead, the host’s webpack runtime initializes sharing scopes and, only when a remote module is needed, fetches the corresponding remoteEntry.js. This script contains a manifest of available modules and their chunk locations.

The runtime relies on __webpack_init_sharing__ to initialize the share scope and __webpack_share_scopes__ to manage resolution of shared modules across federated boundaries. Understanding this network waterfall is essential for debugging load performance in production.


Shared Dependency Strategy

Managing shared dependencies is the most critical aspect of Module Federation. The shared configuration supports several modes:

ModeDescriptionUse Case
SingletonOnly one instance globallyReact, Vue, Angular
Version rangeSemver compatibility checkLibraries with strict APIs
EagerLoaded upfront, not lazyCritical shared utilities
FallbackUsed on version mismatchGraceful degradation

For React applications, singleton mode is mandatory — two React instances in the same DOM cause unpredictable behavior. Combine singleton: true with requiredVersion: '^18.0.0' to ensure all federated modules use a compatible React instance.


Error Handling and Fallbacks

Remote modules can fail due to network issues, deployment errors, or version mismatches. A robust architecture handles these failures gracefully.

const FederatedButton = React.lazy(() =>
  import("catalog/Button").catch(() => import("./fallback/Button"))
);

function App() {
  return (
    <ErrorBoundary fallback={<FallbackUI />}>
      <Suspense fallback={<Skeleton />}>
        <FederatedButton />
      </Suspense>
    </ErrorBoundary>
  );
}

Layered error handling with Suspense for loading states, error boundaries for rendering failures, and local fallback modules ensures remote failures do not break the entire application.


Deployment Patterns

Module Federation enables flexible deployment. Co-located deployment builds each micro-frontend independently but deploys them together. Separate deployment gives each team full autonomy with remotes pointing to live URLs. Versioned deployment uses tagged references for stability.

A typical CI/CD pipeline builds each micro-frontend independently, publishes artifacts to a CDN, and updates host references. Cache invalidation is critical — each deployment should produce unique chunk hashes to prevent stale cache serving outdated modules.


Conclusion

Module Federation offers a production-proven approach to micro-frontend architecture. Start with a single host-and-remote setup, invest in shared dependency management early, and implement robust error handling. Monitor bundle sizes with webpack-bundle-analyzer and trace runtime resolution using browser DevTools. The official Module Federation documentation and Zack Jackson’s example repositories provide excellent starting points.