JS Guide
HomeQuestionsTopicsCompaniesResources
BookmarksSearch

Built for developers preparing for JavaScript, React & TypeScript interviews.

ResourcesQuestionsSupport
HomeQuestionsSearchProgress
HomeQuestionssystem-design
Next

Learn the concept

Frontend Scalability Patterns

system-design
senior
scalability

How would you architect a micro-frontend system?

micro-frontends
module-federation
webpack
scalability
architecture
team-autonomy
independent-deployment
Quick Answer

A micro-frontend architecture decomposes a monolithic frontend into smaller, independently deployable applications owned by autonomous teams, using integration techniques like Webpack Module Federation, Single-SPA, or Web Components, while sharing a design system and communicating through custom events or a shared state bus.

Detailed Explanation

Micro-frontends extend the principles of microservices to the frontend, allowing multiple teams to build, test, and deploy their portions of a web application independently. This is not a default architectural choice — it introduces significant complexity and is best suited for large organizations with multiple autonomous teams working on distinct product domains.

Why Micro-Frontends?

  • Team autonomy — Each team owns a vertical slice (e.g., checkout, search, catalog) end-to-end, from database to UI. They can make technology choices, release on their own cadence, and iterate without coordinating merges into a monolithic codebase.
  • Independent deployments — A change to the checkout flow does not require redeploying the product catalog. This drastically reduces blast radius and rollback complexity.
  • Technology diversity — Teams can incrementally adopt new frameworks. A legacy Angular module can coexist with a new React micro-frontend during a migration.
  • Scalable development — Onboarding new teams does not slow existing ones down. Merge conflicts across unrelated features disappear.

Implementation Approaches

1. Webpack Module Federation (Recommended for most cases)

Module Federation, introduced in Webpack 5, allows separately built applications to share modules at runtime. A host (shell) application declares which remotes it consumes, and each remote exposes specific modules. Shared dependencies like React are loaded once as singletons.

Pros: Native Webpack support, eager/lazy loading, granular sharing, versioned fallbacks. Cons: Tight coupling to Webpack (though Rspack and Vite now have compatible plugins).

2. Single-SPA

A meta-framework that orchestrates multiple single-page applications. Each micro-frontend registers itself with lifecycle hooks (bootstrap, mount, unmount), and Single-SPA activates the correct one based on the URL.

Pros: Framework-agnostic, battle-tested, supports lazy loading. Cons: More boilerplate, shared dependency management is manual.

3. iframes

The simplest isolation model. Each micro-frontend renders in its own iframe with complete DOM and JavaScript isolation.

Pros: Perfect isolation, impossible to leak styles or global state. Cons: Poor UX (no shared scrolling, accessibility challenges), difficult deep linking, performance overhead from multiple browser contexts.

4. Web Components

Each micro-frontend exposes custom elements. The shell app renders them as standard HTML tags.

Pros: Framework-agnostic, native browser support, Shadow DOM style isolation. Cons: Server-side rendering is complex, React's synthetic event system can conflict with Shadow DOM.

Shared Dependencies

Duplicate bundles are the biggest performance concern. Use the singleton pattern to load React, React DOM, and your design system exactly once:

  • In Module Federation, declare shared dependencies with singleton: true and requiredVersion.
  • At build time, mark them as externals and load from a CDN or the shell.
  • Use an import map (<script type="importmap">) to point bare specifiers to shared CDN URLs.

Routing

The shell application owns the top-level router. It maps URL prefixes to micro-frontends (e.g., /checkout/* loads the checkout remote). Within their prefix, each micro-frontend manages its own internal routing. Deep linking works because the shell delegates everything after the prefix.

Communication Between Micro-Frontends

Micro-frontends should be as decoupled as possible. When communication is necessary:

  1. Custom Events — The simplest pattern. One micro-frontend dispatches a CustomEvent on window, and another listens for it. This keeps coupling to an event name contract.
  2. Shared state bus — A lightweight pub/sub or observable store (not Redux — something framework-agnostic) mounted on the shell. Useful for cross-cutting concerns like authentication state or feature flags.
  3. URL parameters — Query params and hash fragments are inherently shareable across micro-frontends.
  4. Props down from shell — The shell passes data to micro-frontends during mount. Suitable for one-time configuration but not reactive updates.

Shared Design System

Visual consistency requires a shared component library published as an npm package or exposed via Module Federation. Key practices:

  • Publish a versioned design system that all micro-frontends depend on.
  • Use design tokens (CSS custom properties) so theming is consistent without runtime coupling.
  • Enforce visual regression testing in the design system CI pipeline.

Deployment Strategies

  • Each micro-frontend has its own CI/CD pipeline, repository (or monorepo workspace), and deployment target.
  • The shell's remote entry URLs can point to versioned artifacts or a latest alias for canary deployments.
  • Use canary deployments with traffic splitting: route 5% of users to the new micro-frontend version, monitor error rates, then promote.
  • A deployment manifest (JSON file mapping micro-frontend names to their current remote entry URLs) lets the shell dynamically resolve remotes without redeployment.

Performance Concerns

  • Bundle duplication — Without singleton sharing, each micro-frontend ships its own React. This can double or triple initial page weight.
  • Loading waterfall — The shell loads, then fetches remote entry points, then fetches the actual chunks. Use <link rel="preload"> for remote entries and critical chunks.
  • Runtime overhead — Multiple framework instances consume more memory and CPU. Measure with Chrome DevTools Performance tab.

When NOT to Use Micro-Frontends

  • Small teams (fewer than 3-4 frontend teams) — The coordination overhead exceeds the autonomy benefit.
  • Tightly coupled features — If every user action touches multiple domains simultaneously, the event-based communication becomes a distributed monolith.
  • Early-stage products — You do not know your domain boundaries yet. Start with a well-structured monolith and extract micro-frontends when organizational pain justifies it.
  • Performance-critical applications — The runtime overhead of multiple framework instances may be unacceptable for applications requiring sub-100ms interactions.

Code Examples

Webpack Module Federation — Host and Remote configurationJavaScript
// webpack.config.js — Remote (Checkout micro-frontend)
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  output: {
    publicPath: 'https://checkout.example.com/',
    uniqueName: 'checkout',
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'checkout',
      filename: 'remoteEntry.js',
      exposes: {
        './CheckoutPage': './src/CheckoutPage',
        './CartWidget': './src/CartWidget',
      },
      shared: {
        react: { singleton: true, requiredVersion: '^19.0.0' },
        'react-dom': { singleton: true, requiredVersion: '^19.0.0' },
        '@company/design-system': { singleton: true },
      },
    }),
  ],
};

// webpack.config.js — Host (Shell application)
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'shell',
      remotes: {
        checkout: 'checkout@https://checkout.example.com/remoteEntry.js',
        catalog: 'catalog@https://catalog.example.com/remoteEntry.js',
        account: 'account@https://account.example.com/remoteEntry.js',
      },
      shared: {
        react: { singleton: true, eager: true, requiredVersion: '^19.0.0' },
        'react-dom': { singleton: true, eager: true, requiredVersion: '^19.0.0' },
        '@company/design-system': { singleton: true, eager: true },
      },
    }),
  ],
};

// Shell app — lazy-loading a remote micro-frontend
const CheckoutPage = React.lazy(() => import('checkout/CheckoutPage'));

function App() {
  return (
    <React.Suspense fallback={<LoadingSkeleton />}>
      <Routes>
        <Route path="/checkout/*" element={<CheckoutPage />} />
      </Routes>
    </React.Suspense>
  );
}

Real-World Applications

Use Cases

Large E-Commerce Platform

Separate teams own checkout, product catalog, search, and user account flows — each deployed independently with their own release cycles and tech stacks

Enterprise Dashboard Suite

Multiple business units contribute analytics widgets, reporting tools, and admin panels to a shared shell without coordination bottlenecks

Incremental Framework Migration

Migrating from Angular to React page by page, with both frameworks running simultaneously under a shell app, rather than a risky big-bang rewrite

Mini Projects

Module Federation Playground

advanced

Build a shell app with two remote micro-frontends (a todo list and a weather widget) using Webpack Module Federation, with shared React and a custom event bus for communication

Deployment Manifest System

advanced

Create a deployment manifest service that allows updating micro-frontend remote entry URLs without redeploying the shell, with rollback capability

Industry Examples

IKEA

Uses micro-frontends to allow dozens of teams to independently develop and deploy parts of their e-commerce experience

Spotify

The Spotify desktop app uses iframe-based micro-frontends to compose independently developed features into a unified experience

Zalando

Pioneered Project Mosaic, a micro-frontend framework powering their e-commerce platform with fragment-based composition

Resources

Webpack Module Federation

docs

Micro Frontends — Martin Fowler

article

micro-frontends.org

article

Single-SPA Documentation

docs

Related Questions

How do you approach a frontend system design interview?

junior
fundamentals

What are the trade-offs between client-side and server-side rendering?

junior
rendering

How do you design a component library / design system for multiple teams?

mid
component-architecture
Next
Design a food delivery tracking page with real-time updates
Next