InkdownInkdown
Start writing

Frontend Concepts Low Level

2 files·0 subfolders

Shared Workspace

Frontend Concepts Low Level
micro-frontend-architecture.md

micro-frontend-architecture

Shared from "Frontend Concepts Low Level" on Inkdown

Micro Frontend Architecture — Complete Deep Dive

Table of Contents

  1. What Micro Frontends Are
  2. The Mental Model
  3. Why Micro Frontends Exist
  4. The Six Integration Strategies
  5. Module Federation — Deep Dive
  6. Communication Between Micro Frontends
  7. Routing Strategies
  8. Styling Isolation
pagination.md
  • Shared Dependencies — The Version Problem
  • Frameworks and Tools
  • Deployment Architecture
  • Build and CI/CD Pipeline
  • Testing Strategies
  • Trade-offs — When to Use, When NOT to Use
  • Real-World Examples
  • Complete Comparison of All Approaches
  • Key Takeaways

  • 1. What Micro Frontends Are

    Micro frontends extend the microservices philosophy to the browser. Instead of one monolithic frontend app, you have multiple independent frontend apps composed into a single user experience.

    Simple Analogy

    Think of a newspaper:

    • Monolith = One giant article written by one person. Everyone waits for that person.
    • Micro frontends = Different sections (sports, politics, weather) written by different teams, assembled into one paper.

    Each section is independent. The sports team doesn't wait for the weather team. They publish on their own schedule. But the reader sees one cohesive newspaper.

    The Technical Definition

    A micro frontend architecture consists of:

    • A shell (host/container) — the main app that provides the page skeleton
    • Multiple micro frontends (remotes/fragments) — independent apps that own specific features
    • An integration mechanism — how the shell loads and composes the micro frontends

    2. The Mental Model

    Plain text

    Each box is a separate app, built, tested, and deployed independently. The shell orchestrates loading and composing them. The user sees a single, unified page.

    Another Way to Visualize
    Plain text

    3. Why Micro Frontends Exist

    The Monolith Pain

    A large frontend monolith becomes painful when:

    ProblemWhat Happens
    Multiple teams, one codebaseMerge conflicts, blocked deploys, "who owns this component?"
    One tech stack locks everyoneTeams can't choose what works best for their domain
    Single deploy bottleneckOne team's change blocks everyone else's release
    Growing build times10-minute builds kill productivity
    Fear of breaking thingsChanging a shared component might break 50 pages
    Big-bang rewrites"Let's rewrite the whole app" → 2-year project that fails
    The Promise of Micro Frontends
    • Independent deployment — Team A deploys their piece without affecting Team B
    • Tech stack freedom — Team A uses React, Team B uses Vue (controversial, more on this later)
    • Team autonomy — Each team owns their feature end-to-end (frontend + backend + deploy)
    • Incremental upgrades — Migrate piece by piece instead of big-bang rewrites
    • Fault isolation — If Team B's widget crashes, the rest of the page still works
    • Smaller bundles per team — Each team only thinks about their own code
    When the Pain Justifies the Complexity

    Micro frontends are NOT for every project. They solve organizational scaling problems, not technical ones. If you have 2-3 frontend developers on one product, a monolith is fine. If you have 10+ teams building one user-facing product, micro frontends start making sense.


    4. The Six Integration Strategies

    There are fundamentally different ways to compose micro frontends. They range from dead-simple to architecturally complex.


    Strategy 1: Iframe (The OG Approach)

    The simplest — just embed apps in iframes.

    Html
    How It Works
    • Each micro frontend is a full page served from its own domain/subdomain
    • The shell positions iframes where each piece should appear
    • Browser naturally isolates JS, CSS, cookies, everything
    Plain text
    Pros
    • Perfect isolation — JS, CSS, globals can't leak between iframes
    • Zero coupling between apps
    • Trivially simple to implement
    • Each team can use anything — doesn't even need to be JavaScript
    Cons
    • iframe has its own document → no shared DOM, no shared events easily
    • Nested scrollbars, sizing issues (iframes don't auto-resize to content)
    • URL/routing is painful — each iframe has its own URL, browser back button breaks
    • SEO is garbage — search engines don't crawl iframes well
    • Performance overhead — each iframe = separate rendering context, separate JS engine context
    • postMessage needed for communication (clunky, string-only)
    • Mobile UX problems — scroll hijacking, touch event issues
    When to Use

    Legacy integration, embedding third-party content (payment widgets, maps), quick prototypes. Not for serious micro frontend architecture.

    Real-World Example

    Stripe's payment elements used to be iframes. So do many ad embeds. This is fine for isolated widgets but not for building a full page.


    Strategy 2: Build-Time Integration (NPM Packages)

    Each team publishes their components as npm packages. The host app installs and imports them.

    Plain text
    JavaScript
    How It Works
    Plain text
    Pros
    • Simple mental model — just imports, like any other code
    • Full TypeScript support across boundaries
    • No runtime overhead — everything is bundled together
    • Teams can iterate independently on their packages
    • Easy to test in isolation (just install the package)
    Cons
    • Not truly independent deployment — host must rebuild and redeploy when any package updates
    • Shared dependencies can cause version conflicts (React 18 vs 17)
    • Bundle size grows as you add more micro frontends
    • Build times grow monotonically (it's still one big build)
    • Coordination needed for shared dependencies and design system changes
    The Version Update Problem
    Plain text
    When to Use

    Small-to-medium orgs that want code separation and team ownership but don't need truly independent deployment. This is the most common "starter" approach and honestly the right choice for many teams.


    Strategy 3: Script Tag / Runtime Integration

    Each micro frontend is loaded at runtime via <script> tags. The shell dynamically loads scripts and mounts apps.

    Html

    Each micro frontend exposes a global mount/unmount API:

    JavaScript

    The shell calls these:

    JavaScript
    How It Works
    Plain text
    The Contract

    Every micro frontend must implement:

    JavaScript
    Pros
    • True independent deployment — each team deploys to their own CDN
    • Shell doesn't need to rebuild when a team updates
    • Natural code splitting (each app is a separate file)
    • Simple to understand
    Cons
    • Global namespace pollution (window.XXX)
    • No TypeScript types across boundaries (or very limited)
    • Need a contract/agreement on the mount/unmount API
    • CSS can leak between apps (no isolation)
    • Shared dependencies must be carefully managed (who loads React?)
    • No native import/export between apps
    • Error in one script can block loading of others
    Handling Shared Dependencies
    Html
    When to Use

    Medium orgs that need independent deployment but don't want the complexity of Module Federation. Also used in legacy modernization where you're gradually migrating from monolith.


    Strategy 4: Web Components

    Each micro frontend is a custom element (Web Component). The shell just uses HTML tags.

    JavaScript
    Html
    How It Works
    Plain text
    Props via Attributes and Events
    JavaScript
    Pros
    • Framework agnostic — custom elements are a browser standard
    • Shadow DOM provides CSS isolation — styles don't leak in or out
    • Works with any framework or no framework
    • Natural HTML composition
    • Can pass data via attributes and listen to custom events
    • Progressive enhancement — works even if JS is slow to load
    Cons
    • Shadow DOM is leaky — modals, portals, tooltips break out of shadow root
    • React doesn't play well with Web Components (event system mismatch, synthetic events don't bubble through shadow DOM)
    • Shadow DOM has performance overhead — separate style calculation, layout
    • No SSR story for many frameworks + Web Components (React SSR with shadow DOM is painful)
    • Attributes can only be strings — need serialization for complex data
    • Accessibility issues — screen readers have trouble with shadow DOM
    • Still need a way to load the scripts that define the custom elements
    • Date inputs, form integration — native form elements don't work inside shadow DOM
    The React + Web Components Problem

    React's event system uses synthetic events attached to the root. Shadow DOM creates a separate DOM tree. React's events don't naturally cross the shadow boundary. This means:

    • onChange, onInput don't fire for shadow DOM inputs
    • Event bubbling stops at shadow boundary (unless composed: true)
    • React's ref doesn't work across shadow boundaries
    • Portals render outside shadow DOM

    You can work around these, but it's friction.

    When to Use

    When you need maximum framework flexibility and teams use different stacks. Works best when:

    • You can tolerate DX friction
    • You don't need React-specific features crossing boundaries
    • CSS isolation is a high priority
    • You're building a design system that must work across frameworks

    Strategy 5: Module Federation (Webpack 5 / Rspack)

    This is the most sophisticated and widely adopted modern approach. Webpack 5 introduced Module Federation — it lets you configure webpack to expose and consume modules across separate builds at runtime.

    This deserves its own deep dive section below.


    Strategy 6: Server-Side Composition

    The composition happens on the server before sending HTML to the browser.

    Technique A: Edge Side Includes (ESI)
    Html

    The CDN/edge server (Akamai, Fastly, Cloudflare Workers) fetches each fragment, assembles the page, and sends complete HTML to the browser.

    Plain text
    Technique B: Node.js Server Composition
    JavaScript
    Technique C: Streaming SSR (Modern Approach)
    JavaScript

    This streams HTML as each section becomes ready. The browser starts rendering immediately.

    Pros
    • Complete HTML on first paint — great for SEO
    • Fast initial load — no client-side JS needed for composition
    • Works for users with JS disabled
    • Each team deploys their server independently
    • Streaming SSR gives best possible Time to First Byte (TTFB)
    Cons
    • Server-side complexity — need a composition layer (CDN config, Node server, etc.)
    • Latency: server must wait for all fragments (unless streaming)
    • Interactivity still needs client-side hydration — it's not purely server-side
    • Harder to do client-side routing/navigation — full page reloads or complex hydration
    • Infrastructure overhead — multiple services, service mesh, health checks, etc.
    • If one fragment is slow, it can block the entire page (without streaming)
    When to Use

    Content-heavy sites where SEO and initial load performance are critical. E-commerce product pages are the classic use case. Zalando, Amazon, and most large e-commerce sites use server-side composition for their product pages.


    5. Module Federation — Deep Dive

    Module Federation is the most powerful and widely adopted approach for modern micro frontends. It deserves special attention.

    The Core Idea

    Module Federation lets you configure webpack (or Rspack) so that:

    • App A can expose some of its modules for other apps to consume
    • App B can consume those modules as if they were local imports
    • The modules are loaded at runtime from App A's CDN
    • Shared dependencies (like React) are negotiated and deduplicated
    The Setup
    Remote App (Team B — provides a module)
    JavaScript
    Host App (Shell — consumes remote modules)
    JavaScript
    Consuming Remote Modules in Code
    JavaScript
    What Happens Under the Hood

    This is the most important part to understand deeply.

    Plain text
    The shared Configuration — Critical Detail

    The shared config is where most bugs happen. Let's break it down:

    JavaScript
    singleton: true

    Ensures only one copy of the module exists in the page. If host has React 18.2 and remote wants React 18.x, they share the host's copy. If singleton: false, each app might load its own copy → two React instances → hooks break.

    requiredVersion: '^18'

    Semver range. If the version available doesn't satisfy this range, webpack loads a separate copy (fallback). This is the safety net.

    eager: true vs eager: false
    Plain text
    Version Negotiation Scenarios
    Plain text
    Bi-Directional Sharing

    Module Federation isn't just host→remote. Remotes can also consume from the host:

    JavaScript
    Dynamic Remotes (Runtime Configuration)

    Instead of hardcoding remote URLs at build time, you can load them dynamically:

    JavaScript

    This lets you change remote URLs without rebuilding the shell — useful for:

    • Environment-specific URLs (staging vs production)
    • A/B testing different versions of a micro frontend
    • Feature flags for micro frontend versions
    Error Handling — When a Remote Fails
    JavaScript
    Module Federation v2 (Enhanced)

    Webpack's Module Federation has evolved. The enhanced version adds:

    • Type sharing — TypeScript types are shared across boundaries
    • Lifetime management — better control over when remotes load/unload
    • Dev toolbar — visualize loaded remotes and shared modules
    • Bridge — better integration between different bundlers (webpack + Vite)
    Rspack and Vite Support
    • Rspack (Rust-based webpack alternative): Native Module Federation support, much faster builds
    • Vite: Community plugin @originjs/vite-plugin-federation — works but not as battle-tested
    • Rsbuild (build tool by ByteDance): Built-in Module Federation support

    6. Communication Between Micro Frontends

    This is one of the hardest parts. Independent apps need to talk to each other. How?

    Pattern 1: Custom Events (Pub/Sub via DOM)
    JavaScript
    Analysis

    Pros: Simple, no shared code, browser native, zero dependencies Cons: Hard to debug (events are invisible in component tree), no type safety, event names become implicit contracts, memory leaks if you forget to unsubscribe

    Best Practice — Create a Typed Event Bus Wrapper
    JavaScript
    Pattern 2: Shared State Store

    All micro frontends read/write to a common store.

    JavaScript
    The Critical Decision — How Is the Store Shared?
    MethodHowTrade-off
    NPM packageEach MFE installs @org/shared-stateBuild-time coupling, version management
    Module Federation sharedHost provides the store instanceRuntime, requires MF config
    Shell provides via mount propsShell passes store instance to mount()Explicit but verbose
    Global singletonStore on window.__STORE__Quick and dirty, no type safety
    The Singleton Store Problem

    If each MFE bundles its own copy of Zustand/Redux, they each have their own store instance. Updates in one aren't visible in another. You MUST ensure there's exactly one store instance.

    JavaScript
    Pattern 3: URL as Communication

    Apps communicate through the URL. This is underrated and often the simplest solution.

    Plain text
    • Product listing updates the URL when user filters
    • Cart widget reads the URL and renders accordingly
    • No shared state library needed

    Pros: Shareable state, works with browser back/forward, SEO-friendly, zero coupling Cons: Limited data capacity (URL length ~2000 chars), serialization overhead, not great for large/complex state

    Pattern 4: Callback Props (Parent-Child)

    The shell passes callbacks to micro frontends:

    JavaScript

    Pros: Explicit, type-safe, easy to reason about Cons: Only works for direct parent-child relationships, shell must know about all events, doesn't scale for cross-cutting concerns

    Pattern 5: Shared BroadcastChannel (Cross-Tab Communication)

    For communication between tabs/windows of the same app:

    JavaScript
    Summary — Which Pattern When?
    PatternBest ForScale
    Custom Events / Event BusDecoupled communication, any-to-anySmall to large
    Shared State StoreComplex shared state (cart, auth, user)Medium to large
    URLFilter/search state, bookmarkable stateAny
    Callback PropsSimple parent-child communicationSmall
    BroadcastChannelCross-tab syncSpecialized

    Real-world apps use a combination. URL for navigation/filter state, shared store for cart/auth, event bus for cross-cutting events.


    7. Routing Strategies

    Routing in micro frontends is tricky because you have multiple apps that each might want to control the URL.

    Strategy A: Shell Owns All Routes
    JavaScript

    Each micro frontend doesn't know about routing — the shell decides what to render where.

    Pros: Simple, centralized control, easy to reason about Cons: Shell becomes a bottleneck, must be updated for every new route, teams can't add routes autonomously

    Strategy B: Each Micro Frontend Owns Its Routes
    JavaScript

    Pros: Teams control their own routes, shell is thinner, true autonomy Cons: URL structure must be pre-agreed, deep linking needs coordination, debugging routing issues is harder

    Strategy C: Route Registry (Dynamic)

    Micro frontends register their routes with the shell at runtime:

    JavaScript

    Pros: Fully dynamic, no shell redeployment needed for new routes, true plug-and-play Cons: Complex, hard to debug routing issues, no static analysis, route conflicts possible

    Strategy D: Path Prefix per Team

    Each team gets a URL prefix. No coordination needed.

    Plain text

    The shell is thin — it just matches prefixes and delegates:

    JavaScript

    Pros: Zero routing conflicts, simple, teams are fully autonomous Cons: URL structure is rigid, can't have routes that span multiple MFes naturally

    Handling the "Active App" Problem

    When using Strategy B or D, only one MFE is "active" at a time. The active MFE's router handles the URL. The inactive ones should not try to read the URL.

    JavaScript

    8. Styling Isolation

    One of the biggest practical challenges. If Team A uses Tailwind and Team B uses Bootstrap, styles will collide.

    The Problem
    Css
    Approach 1: CSS Modules (Recommended Default)
    Css
    JavaScript

    Class names are hashed to unique strings. No collisions possible.

    Pros: Simple, zero runtime overhead, great TypeScript support Cons: No style sharing between MFes (each has its own hashed classes)

    Approach 2: CSS-in-JS with Scoped Selectors
    JavaScript

    Pros: Dynamic styles, theming, no class name management Cons: Runtime overhead, larger bundle, SSR complexity, multiple CSS-in-JS libraries = multiple runtime instances

    Approach 3: Shadow DOM

    Web Components with Shadow DOM isolate CSS completely:

    JavaScript

    Trade-offs already covered in the Web Components section.

    Approach 4: BEM / Naming Convention Agreement
    Css

    Pros: Simple, no build tool needed, works everywhere Cons: Relies on discipline, no enforcement, verbose

    Approach 5: Design Tokens + Shared Component Library

    Instead of fighting CSS, agree on a design system:

    Css
    JavaScript

    Pros: Visual consistency, no CSS collisions, shared language Cons: Design system must be maintained, changes require coordination, some teams may need customizations beyond what the system offers

    The Recommended Approach

    Combine CSS Modules (for isolation) + Design Tokens (for consistency):

    Plain text

    9. Shared Dependencies — The Version Problem

    The #1 technical challenge in micro frontends.

    The Scenario
    Plain text
    What Happens with Each Strategy
    StrategyBehavior
    Build-time (npm)npm/yarn dedupes to one version (usually highest). Team C's React 17 code might break.
    Module FederationShared config negotiates. Team C's requiredVersion: '^17' won't match 18.2 → loads its own copy.
    Script tag / externalsEach app bundles its own. Multiple instances guaranteed.
    IframeComplete isolation. No conflict possible.
    The Singleton Problem

    React, Vue, and most frameworks break with multiple instances.

    Plain text
    Solutions
    Solution 1: Enforce Single Version (Organizational)
    Plain text
    Solution 2: Module Federation Shared Config
    JavaScript
    Solution 3: Externals + CDN
    Html
    Solution 4: Iframe Isolation (Nuclear Option)

    Each MFE has its own document → its own React instance. No conflict. But you lose all benefits of sharing.

    Dependency Compatibility Matrix
    DependencySingleton Required?Why?
    ReactYESHooks and Context break with multiple instances
    React DOMYESTied to React version
    React RouterYESMultiple routers fight over URL
    ReduxDependsMultiple stores = separate state (might be intentional)
    ZustandDependsSame as Redux
    styled-componentsNoEach instance generates its own classes (but duplicates CSS)
    TailwindNoCSS-only, no runtime
    LodashNoPure functions, stateless
    date-fnsNoPure functions, stateless
    AxiosNoEach instance works independently

    10. Frameworks and Tools

    single-spa

    The original micro frontend framework. Framework-agnostic router that orchestrates mounting/unmounting.

    JavaScript

    Each MFE must export lifecycle functions:

    JavaScript

    Pros: Mature, framework-agnostic, handles routing and lifecycle, large ecosystem Cons: Doesn't handle shared dependencies, no built-in module sharing, requires SystemJS for script loading, older architecture

    Piral

    Built on top of single-spa. Adds a feed service for discovering micro frontends at runtime.

    Plain text

    How discovery works:

    1. Shell fetches list of available MFEs from a feed service
    2. Feed service returns metadata (name, version, URL, dependencies)
    3. Shell loads and registers them
    4. MFes can also register extensions in named "slots" (e.g., header extensions, dashboard widgets)

    Pros: Full platform with auth, notifications, extensions, tile-based dashboard Cons: Opinionated, heavy, steep learning curve, Piral-specific concepts

    Luigi (by SAP)

    Micro frontend framework with web component support and a built-in development console.

    JavaScript

    Pros: Good for enterprise, iframe-based isolation, SAP ecosystem integration Cons: iframe-based (inherits iframe limitations), SAP-centric

    Open Components (oc)

    Server-side rendering focused micro frontend framework. Each component is a small Node.js service that renders HTML.

    Bit

    Component-driven development platform. Each component is independently versioned and shared.

    Bash

    Pros: Granular (component-level, not app-level), great DX Cons: More suited for component libraries than full micro frontends

    Comparison Table
    Featuresingle-spaPiralLuigiModule Federation
    Runtime routingYesYesYesNo (host handles)
    Shared depsNoYesNoYes (built-in)
    Framework agnosticYesPartialYesNo (bundler-specific)
    DiscoveryStatic configFeed serviceConfig fileStatic config
    SSR supportLimitedYesIframe onlyWith setup
    ComplexityMediumHighMediumMedium-High
    MaturityHighMediumMediumHigh
    Learning curveMediumSteepLow-MediumSteep
    Best forAny stackFull platformEnterprise/SAPReact/Vue shops

    11. Deployment Architecture

    The Ideal Deployment Flow
    Plain text
    Key Principles
    1. Each team deploys independently — Team B can deploy 5x/day while Team A deploys weekly
    2. CDN-based — all assets served from CDN for performance
    3. Versioned URLs — remoteEntry.v3.js or hash-based (remoteEntry.a1b2c3.js) for cache busting
    4. Shell references remotes by URL — shell knows WHERE to find each remote
    The Version Discovery Problem

    How does the shell know which version of each remote to load?

    Plain text
    Rollback Strategy
    Plain text
    CDN Cache Strategy
    Plain text

    12. Build and CI/CD Pipeline

    Per-Team Pipeline

    Each team has their own CI/CD pipeline:

    YAML
    Shared CI Checks

    Organization-level checks that run on ALL MFE repos:

    YAML
    Dependency Update Strategy
    Plain text

    13. Testing Strategies

    Testing Levels
    Plain text
    Unit Testing (Per MFE)

    Each team tests their MFE in isolation:

    JavaScript
    Integration Testing (MFE + Shell)

    Test that the MFE works correctly when loaded by the shell:

    JavaScript
    E2E Testing (Cross-MFE)

    Test complete user journeys that span multiple micro frontends:

    JavaScript
    Contract Testing

    Test that MFes honor their contracts (mount/unmount API, event bus events, shared state):

    JavaScript
    Testing Gotchas
    • Mock remote modules in unit tests (don't try to load real remotes)
    • Use a test harness for integration tests that simulates the shell
    • E2E tests should run against deployed staging environment (not local)
    • Version pin remotes in E2E tests to avoid flaky tests from remote updates
    • Test with remote offline — what happens when Team B's CDN is down?

    14. Trade-offs — When to Use, When NOT to Use

    When Micro Frontends Make Sense
    SignalDescription
    10+ frontend developersMultiple teams stepping on each other's code
    Independent team deploymentTeam A can't wait for Team B's release cycle
    Distinct business domainsShop, Account, Checkout are clearly separable
    Long-lived productYou'll maintain this for years, worth the investment
    Incremental migration neededMoving from monolith to microservices gradually
    Different tech needs per domainCheckout needs high security, Shop needs rich interactivity
    When Micro Frontends Are Overkill
    SignalDescription
    < 5 frontend developersCommunication is easy, monolith is fine
    Single product, single teamNo organizational scaling problem
    Tight coupling between featuresIf everything depends on everything, separation is artificial
    Short-lived projectNot worth the infrastructure investment
    Team doesn't have DevOps maturityMicro frontends need CI/CD, CDN, monitoring
    "We want to use different frameworks"Usually not a good enough reason alone
    The Honest Trade-offs
    BenefitCost
    Independent deploymentMore infrastructure (CDNs, config APIs, monitoring)
    Team autonomyCoordination overhead (contracts, shared libs, design system)
    Tech stack freedomShared dependency hell, inconsistent UX
    Fault isolationMore complex error handling, more failure modes
    Smaller per-team bundlesMore HTTP requests, more complex loading
    Incremental upgradesVersion management across N repos
    The 80/20 Rule

    Most teams get 80% of the benefit from build-time integration (npm packages) with clear team ownership. Only go to runtime integration (Module Federation) when you truly need independent deployment.


    15. Real-World Examples

    Amazon
    • One of the original micro frontend adopters
    • Product page = composed from 100+ server-side rendered fragments
    • Each team (pricing, reviews, recommendations, buy box) owns their fragment
    • Server-side composition (similar to ESI)
    • Each fragment is a separate service
    Zalando
    • Pioneer of the micro frontend concept
    • Published extensively about their approach
    • Uses iframe-based approach for team isolation
    • Each team owns their page section end-to-end
    • Published the "Micro Frontends" book on the topic
    Spotify
    • Desktop app uses micro frontends
    • Different features (player, library, search) are separate modules
    • Uses a custom module system (not Module Federation)
    • Teams deploy independently
    TikTok / ByteDance
    • Uses Module Federation at massive scale
    • Created Rsbuild/Rspack (Rust-based webpack alternative) partly to improve MF build times
    • Multiple consumer apps composed from micro frontends
    Upwork
    • Uses single-spa for their freelancer platform
    • Migrated from AngularJS monolith to micro frontends
    • Each product area (search, proposals, messaging) is a separate MFE
    • Published their migration story
    Shopify
    • Admin panel uses micro frontends (Polaris)
    • Extension system allows third-party developers to add UI to admin
    • Uses Web Components for extensions (framework-agnostic)
    IKEA
    • Uses micro frontends for their e-commerce platform
    • Different markets can customize their storefronts
    • Shared components with market-specific customization

    16. Complete Comparison of All Approaches

    CriteriaIframeNPM PackagesScript TagWeb ComponentsModule FederationServer-Side
    Independent DeployYesNoYesYesYesYes
    JS IsolationPerfectNoneNoneShadow DOMShared scopeN/A
    CSS IsolationPerfectNoneNoneShadow DOMManualManual
    Shared DepsNoYes (deduped)ManualNoAutomaticN/A
    TypeScriptNoFullNoLimitedGoodN/A
    SSRNoYesNoPartialComplexYes (native)
    SEOBadGoodGoodGoodGoodBest
    PerformanceWorstBest (single bundle)GoodGoodGoodBest (SSR)
    DXBadBestMediumMedium-HardMedium-HardMedium
    ComplexityLowLowMediumMediumHighHigh
    Framework LockNoneSame frameworkSame frameworkNoneSame frameworkAny
    MaturityAncientStandardStandardStandardModernStandard
    Best For3rd party embedsSmall orgsMedium orgsMixed stacksLarge orgsContent sites

    17. Key Takeaways

    1. Micro frontends solve organizational problems, not technical ones. If your team is small, use a monolith.

    2. Start with build-time integration (npm packages). Only move to runtime integration when you truly need independent deployment.

    3. Module Federation is the modern standard for runtime micro frontends in React/Vue ecosystems. Understand the shared config deeply.

    4. Communication is the hardest part. Use a typed event bus for decoupled communication and a shared store for complex shared state.

    5. CSS isolation matters. CSS Modules + Design Tokens is the pragmatic sweet spot.

    6. Shared dependencies must be managed carefully. Singleton frameworks (React, Vue) MUST have only one instance.

    7. Deployment independence is the whole point. If you can't deploy independently, you don't have micro frontends — you have a distributed monolith.

    8. Don't mix frameworks unless you have a very good reason. The "framework freedom" promise sounds great but creates enormous operational overhead.

    9. Server-side composition is best for SEO/performance. Client-side composition is best for interactivity. Many production apps use both.

    10. The shell should be thin. If the shell grows large, you've just rebuilt the monolith with extra steps.


    Quick Decision Guide

    Plain text