smaple.tr
micro frontend architecture

Micro Frontend Architecture: Module Federation, Single-SPA, and Independent Deployment [2026]

Mehmet Kurtipek
November 24, 2025
12 min read
micro frontend architecture
module federation
single-spa
web components
independent deployment
frontend composition
shell application

At three frontend developers, you understand your entire codebase. At thirty developers, the frontend has become the bottleneck: two teams cannot deploy independently, a design system change requires coordinating five teams, and adding a new feature in one area accidentally breaks another. Micro frontend architecture applies microservices principles to the frontend — decomposing a monolithic UI into independently deployable pieces owned by separate teams.

This guide covers the full micro frontend architecture stack: composition approaches (module federation, single-spa, web components, server-side composition), routing and navigation, shared state management, CSS isolation, performance optimization, testing strategy, and CI/CD patterns. By the end, you have a practical decision framework for when micro frontends create value and how to implement them effectively.

Micro Frontend Architecture: When It Makes Sense

The central value proposition of micro frontend architecture is team autonomy: independent deployment, independent technology decisions, and independent development velocity for each team.

The threshold for justified adoption: three or more independent teams working on overlapping product surfaces, where coordination cost has become a bottleneck. Below this threshold, the integration complexity and infrastructure overhead of micro frontends exceeds the value of team independence.

Micro frontends work well for:

  • Large e-commerce platforms (catalog team, checkout team, account team, search team)
  • Enterprise dashboards with distinct business domains (HR portal, finance portal, analytics)
  • Platform products where multiple teams own different feature areas with separate deployment requirements

Micro frontends are wrong for:

  • Single team, single product, early stage
  • Systems where performance is critical (micro frontends increase initial load time by default)
  • Teams without distributed systems experience (micro frontends require operational maturity)

Composition Approaches

Webpack Module Federation (Runtime Composition)

Module Federation (Webpack 5+) is the dominant runtime composition approach. Each micro frontend compiles independently and exposes modules that the shell application (host) loads dynamically at runtime.

Host configuration — the shell application that orchestrates micro frontends:

// webpack.config.js (shell)
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'shell',
      remotes: {
        catalog: 'catalog@https://catalog.example.com/remoteEntry.js',
        checkout: 'checkout@https://checkout.example.com/remoteEntry.js',
        account: 'account@https://account.example.com/remoteEntry.js',
      },
      shared: {
        react: { singleton: true, requiredVersion: '^18.0.0' },
        'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
        'react-router-dom': { singleton: true, requiredVersion: '^6.0.0' },
      },
    }),
  ],
};

Remote configuration — a micro frontend that exposes components:

// webpack.config.js (catalog micro frontend)
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'catalog',
      filename: 'remoteEntry.js',
      exposes: {
        './ProductList': './src/components/ProductList',
        './ProductDetail': './src/components/ProductDetail',
      },
      shared: {
        react: { singleton: true, requiredVersion: '^18.0.0' },
        'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
      },
    }),
  ],
};

Consuming a federated module in the shell:

import React, { Suspense, lazy } from 'react';

const ProductList = lazy(() => import('catalog/ProductList'));

function CatalogPage() {
  return (
    <Suspense fallback={<div>Loading catalog...</div>}>
      <ProductList />
    </Suspense>
  );
}

Shared dependencies are the critical configuration. Marking React as singleton: true ensures only one React instance runs in the page. Multiple React instances cause hooks to fail and context to be inaccessible across micro frontend boundaries. The requiredVersion constraint prevents version mismatches that cause silent failures.

Version management: when the shell and a remote specify different requiredVersion ranges that do not overlap, Module Federation downloads both versions — negating the deduplication benefit. Maintain shared dependency versions in sync across all micro frontends. A monorepo or a shared dependency manifest helps.

Alternatives to Webpack Module Federation: Rspack (Rust-based Webpack drop-in with 10x faster builds), Vite Federation plugin (vite-plugin-federation), Native Federation. In 2026, Rspack has become viable for production; evaluate build performance against your specific codebase size.

Single-SPA (Multi-Framework Composition)

Single-SPA is a meta-framework for running multiple JavaScript frameworks in a single page. Each micro frontend registers as a single-spa application with routing-based activation:

import { registerApplication, start } from 'single-spa';

registerApplication({
  name: '@team/catalog',
  app: () => System.import('@team/catalog'),
  activeWhen: ['/products'],
});

registerApplication({
  name: '@team/checkout',
  app: () => System.import('@team/checkout'),
  activeWhen: ['/checkout', '/cart'],
});

registerApplication({
  name: '@team/account',
  app: () => System.import('@team/account'),
  activeWhen: (location) => location.pathname.startsWith('/account'),
});

start({ urlRerouteOnly: true });

Single-SPA provides lifecycle management (bootstrap, mount, unmount) and framework adapters for React, Vue, Angular, Svelte, and plain JavaScript. Each registered application mounts when its activeWhen condition is true and unmounts when it is false.

When to choose single-spa over Module Federation:

  • The team needs to run multiple JavaScript frameworks in a single page (React + Angular, Angular + Vue during a framework migration)
  • Import Maps-based module loading is preferred over bundler-level federation
  • Granular lifecycle management is needed

Import Map Overrides — the import-map-overrides tool allows developers to override individual micro frontends with local builds during development, without running the full shell locally. Essential for local development productivity in micro frontend architectures.

Web Components (Framework-Independent)

Web Components (Custom Elements + Shadow DOM + HTML Templates) provide a browser-native composition mechanism. Each micro frontend is a custom element; any framework can render it.

class ProductCatalogElement extends HTMLElement {
  connectedCallback() {
    const root = this.attachShadow({ mode: 'open' });
    // Mount React/Vue/Angular app into shadow root
    ReactDOM.createRoot(root).render(<ProductCatalog />);
  }

  disconnectedCallback() {
    // Cleanup
  }
}

customElements.define('product-catalog', ProductCatalogElement);

Shadow DOM provides CSS isolation automatically — styles inside the shadow root do not leak out, and global styles do not apply inside it (with exceptions for inherited CSS properties).

Shadow DOM limitations: React's synthetic event system does not work correctly with Shadow DOM in all cases. Form elements in Shadow DOM require special handling. Server-side rendering with Shadow DOM requires Declarative Shadow DOM (browser support growing in 2026 but not universal). These limitations make Web Components more suitable as a composition mechanism for isolated leaf components rather than full application sections.

Server-Side Composition

Server-side composition assembles the HTML from multiple micro frontend services on the server before delivery to the client. Two approaches:

Edge Side Includes (ESI) — CDN-level composition. The edge server fetches fragments from micro frontend services and assembles the final HTML. Used by Taobao, IKEA, and other large platforms for maximum performance.

Gateway composition — a dedicated composition service fetches fragments from micro frontend services and assembles them. Provides a single response to the client with no client-side JavaScript required for composition.

Server-side composition significantly improves initial load performance and SEO (full HTML available to crawlers). Trade-off: micro frontends must expose server-rendered HTML fragments, adding a server rendering requirement.

When to use: applications with strict SEO requirements (marketing pages, content sites), performance-critical initial load, or environments where JavaScript bundle size is a significant concern.

Routing and Navigation

Shell-Managed Routing

The shell application owns top-level routing. When a URL matches a micro frontend's domain, the shell mounts the corresponding micro frontend. Each micro frontend manages its own internal routing:

// Shell: route to micro frontends
<Routes>
  <Route path="/products/*" element={<CatalogMicrofrontend />} />
  <Route path="/checkout/*" element={<CheckoutMicrofrontend />} />
  <Route path="/account/*" element={<AccountMicrofrontend />} />
</Routes>

// Catalog micro frontend: internal routes
<Routes>
  <Route path="/" element={<ProductList />} />
  <Route path="/:productId" element={<ProductDetail />} />
  <Route path="/category/:slug" element={<CategoryPage />} />
</Routes>

The shell's * wildcard on each top-level route passes all sub-path routing to the micro frontend. The micro frontend uses relative paths for its internal routes.

Deep linking: users must be able to navigate directly to any URL, including deep links within micro frontends. Test: if a user bookmarks https://shop.example.com/products/shoes/running, can they return to that exact page after the browser fully refreshes? This requires the micro frontend to initialize correctly from any route, not just from the root.

Browser history: all micro frontends must use the same history instance to avoid multiple History API entries for a single navigation. In Module Federation, share the history package as a singleton. In single-spa, the framework provides shared history.

Shared State Management

Minimizing Shared State

The primary design rule for shared state in micro frontend architecture: minimize it. Shared state creates coupling. The more state shared between micro frontends, the more coordination is required for changes, and the closer the architecture drifts toward a distributed monolith.

What genuinely requires sharing:

  • Authentication: user identity and session token needed by all micro frontends
  • Feature flags: which features are enabled for the current user/session
  • Global notifications: system-wide alerts or messages
  • Shopping cart (in e-commerce): cart state needed across catalog, checkout, and account micro frontends

What should remain local:

  • Component state (form inputs, modal open/close)
  • UI preferences specific to one micro frontend
  • Data fetched from APIs (each micro frontend fetches its own data)

Sharing State Patterns

Custom Events (browser-native, no dependencies):

// Publish: user added item to cart
window.dispatchEvent(new CustomEvent('cart:item-added', {
  detail: { productId: '123', quantity: 1 }
}));

// Subscribe: cart indicator in header micro frontend
window.addEventListener('cart:item-added', (event) => {
  updateCartCount(event.detail);
});

Custom events decouple micro frontends completely — neither knows the other exists. Limitation: events are fire-and-forget; latecomers miss events that occurred before they mounted.

Shared state store (for persistent shared state):

// Shell: provide auth context to all micro frontends
window.__sharedState = {
  auth: {
    userId: user.id,
    token: user.accessToken,
    subscribe: (callback) => authEventBus.on('change', callback),
  }
};

// Any micro frontend: consume auth state
const authState = window.__sharedState?.auth;

Namespacing under window.__sharedState (or similar) prevents accidental property conflicts. Document the shared state interface as a contract — changes require coordination.

CSS Isolation Strategies

CSS conflicts — one micro frontend's styles breaking another's — are a predictable micro frontend problem. Four approaches in order of reliability:

CSS Modules (compile-time scoping): build tooling generates unique class names per micro frontend. No runtime overhead; reliable isolation. Requires all micro frontends to use CSS Modules consistently.

CSS-in-JS (styled-components, Emotion): styles are scoped to components by default. Class name generation prevents conflicts. Runtime overhead (style injection on render). Works across micro frontends without coordination.

Shadow DOM: native browser isolation. Styles inside the shadow root do not affect the outside; external styles do not apply inside. Complete isolation but with React event system limitations (see Web Components section).

BEM naming convention (discipline-based): prefix all class names with the micro frontend identifier (catalog-product-card, checkout-form-input). No tooling required; relies entirely on team discipline. Does not prevent accidental collisions if naming conventions are not followed strictly.

Recommendation: use CSS Modules or CSS-in-JS for component-level styling within each micro frontend. Coordinate shared design tokens (colors, typography, spacing) through a published NPM package consumed by all micro frontends.

Performance Optimization

Bundle Size Management

The primary micro frontend performance concern is bundle size. Each micro frontend has its own bundle; without proper shared dependency management, the same library (React, lodash, axios) loads multiple times.

Module Federation shared dependencies deduplicate libraries across micro frontends. Measure: compare total JavaScript payload with and without sharing configured. A typical React e-commerce application without sharing: React loads 6 times = 180 KB of duplicated React per page. With singleton sharing: React loads once.

Lazy loading: micro frontends should load only when the user navigates to their route. Module Federation's dynamic import ensures the catalog bundle does not load until the user visits a product page.

Bundle size budgets: set maximum bundle size limits (excluding shared dependencies) per micro frontend. Enforce in CI:

// webpack.config.js
performance: {
  maxAssetSize: 250000,     // 250 KB per asset
  maxEntrypointSize: 400000 // 400 KB per entry point
}

Prefetching

Anticipate navigation and prefetch micro frontends before the user clicks:

// Prefetch checkout micro frontend when user adds to cart
function onAddToCart() {
  import('checkout/CartSummary');  // Prefetch without rendering
  addItemToCart(product);
}

Prefetch on hover over navigation links provides noticeable perceived performance improvement with minimal additional data transfer.

Testing Strategy

Test Layer Architecture

Unit tests: each micro frontend tests its components and business logic independently. Standard testing tools (Jest, Vitest, Testing Library). Zero dependency on other micro frontends.

Contract tests: verify that micro frontends fulfill their contracts with the shell and with each other. Pact or similar consumer-driven contract testing tools. Prevents the shell from calling a micro frontend API that has changed without notification.

Integration tests: test that micro frontends compose correctly when loaded together. Mock shared state, verify event communication between micro frontends, test routing across micro frontend boundaries.

End-to-end tests: user journey tests across the full application. Playwright or Cypress testing the assembled application. These tests are the most expensive to maintain and should focus on critical user paths (purchase flow, account creation, checkout).

Visual regression tests: Chromatic, Percy, or BackstopJS. Prevents CSS changes in one micro frontend from unexpectedly affecting the visual appearance of others. Run on PR for all micro frontends.

Local Development

The standard developer workflow in micro frontend architecture:

  1. Clone the shell repository and start it locally
  2. Use Import Map Overrides (single-spa) or local Module Federation config to point one micro frontend at local dev server
  3. All other micro frontends load from deployed staging URLs

This allows a developer to work on one micro frontend while testing it in context of the full application, without running the entire platform locally.

CI/CD and Independent Deployment

Pipeline Architecture

Each micro frontend has an independent pipeline:

# catalog micro frontend pipeline
stages:
  - test
  - build
  - deploy

test:
  script:
    - npm run test
    - npm run test:e2e        # Integration tests for this MFE only

build:
  script:
    - npm run build
    - aws s3 sync ./dist s3://catalog-mfe/

deploy:
  script:
    - invalidate_cdn_cache catalog.example.com/remoteEntry.js

The shell pipeline includes integration tests that verify the full composition:

integration-tests:
  needs: []    # Run against deployed micro frontends, not local builds
  script:
    - npm run test:integration
    - npm run test:e2e

Deployment Safety

Canary deployment at the shell level: new micro frontend versions can be deployed to a percentage of users by routing different user segments to different remoteEntry.js URLs. This requires the shell to implement feature flag-based remote URL selection.

Rollback: in Module Federation, rollback is updating the remote URL in the shell to point to the previous version's remoteEntry.js. This is a configuration change (instant) rather than a deployment (minutes). Maintain versioned remoteEntry.js files in object storage rather than overwriting; rollback is then URL update without re-deployment.

Version coordination: the shell's shared configuration specifies requiredVersion ranges. A micro frontend that upgrades a shared dependency outside the compatible range will cause the shell to load two versions of that dependency. Establish a version coordination process: post in the micro frontends channel before upgrading major versions of shared dependencies.

Related Articles

August 11, 2026

MLOps Guide: Taking Machine Learning Models to Production [2026]

87% of machine learning models built by data science teams never reach production. The models work — they pass cross-validation, they score well on holdout sets, they demonstrate genuine predictive value. The problem is not the modeling. The problem is everything that happens between a notebook experiment and a reliable, monitored, production system. MLOps is the discipline that closes that gap. This guide covers the full MLOps stack: maturity levels, tooling choices (MLflow, DVC, Kubeflow

Read More: MLOps Guide: Taking Machine Learning Models to Production [2026]
August 9, 2026

Computer Vision Applications: Object Detection, OCR, and Industrial AI [2026]

Computer vision has moved well past the research phase. The models are trained, the frameworks are mature, the hardware is accessible, and the use cases are generating measurable returns. What was a specialized capability requiring deep expertise in 2018 is now deployable infrastructure — if you know which component to reach for and where the real complexity lives. This guide covers computer vision applications across industrial, medical, logistics, and document processing domains. It expl

Read More: Computer Vision Applications: Object Detection, OCR, and Industrial AI [2026]
August 4, 2026

AI Agent Development Guide: Building Autonomous Systems with LangChain and AutoGen [2026]

Large language models that only respond to prompts have a ceiling on the value they can provide. An LLM that can answer questions is useful; an LLM that can plan a multi-step task, execute tools, observe outcomes, and revise its approach is transformative. This is the difference between a language model and an AI agent. In 2026, AI agents have moved from research demonstrations to production deployments across customer service, software engineering, financial analysis, and operations workflo

Read More: AI Agent Development Guide: Building Autonomous Systems with LangChain and AutoGen [2026]