Software

Micro-Frontends: Architecture and Implementation

Technical patterns for scaling frontend teams: module federation, Web Components, and independent deployments.

All articles
SoftwareNexaEx TeamMarch 1, 2026 9 min read
Micro-Frontends: Architecture and Implementation

Micro-Frontends: The Frontend Answer to Microservices

Micro-frontends let independent teams own frontend features end-to-end. This guide covers architecture patterns and real-world trade-offs.

Why Micro-Frontends?

Team autonomy: Teams own a feature slice (checkout flow, dashboard, settings) from backend to UI. No coordination needed.

Independent deployment: Features deploy independently. One team's bug doesn't block others.

Technology flexibility: Different parts can use different frameworks (React, Vue, Svelte). Possible but usually avoided.

Architecture Patterns

1. Module Federation (Webpack 5)

The host app imports modules from remote apps at runtime.

// host app webpack.config.js
new ModuleFederationPlugin({
  name: 'host',
  remotes: {
    checkout: 'checkout@http://localhost:3001/remoteEntry.js',
    dashboard: 'dashboard@http://localhost:3002/remoteEntry.js',
  },
  shared: ['react', 'react-dom'],
})

// host app
async function bootstrap() {
  const CheckoutApp = await import('checkout/App')
  const DashboardApp = await import('dashboard/App')
  ReactDOM.render(<App />, document.getElementById('root'))
}

Advantages: Share dependencies (React, Redux), low overhead, clean interface. Disadvantages: Learning curve, Webpack-dependent, complex debugging.

2. Web Components

Encapsulate UI in custom HTML elements.

// checkout-app.js
class CheckoutElement extends HTMLElement {
  connectedCallback() {
    const root = ReactDOM.createRoot(this)
    root.render(<CheckoutApp />)
  }
}
customElements.define('app-checkout', CheckoutElement)

// host app HTML
<app-checkout></app-checkout>

Advantages: Framework-agnostic, browser standard, no build tool dependency. Disadvantages: Heavier than module federation, style isolation tricky, browser support varies.

3. iframes

Embed each app in an iframe.

Advantages: Complete isolation, supports any framework, oldest pattern (battle-tested). Disadvantages: Heavy overhead, communication slow (postMessage), styling difficult.

Only use iframes for truly isolated features or third-party content.

Shared State Management

Multiple frontends need shared state. Options:

1. Event-based communication:

// checkout-app emits event
window.dispatchEvent(new CustomEvent('checkout-complete', { detail: { orderId: '123' } }))

// host app listens
window.addEventListener('checkout-complete', (e) => {
  console.log('Order:', e.detail.orderId)
})

2. Global state store:

All micro-frontends read/write to shared Redux or Zustand store.

// shared store
export const checkoutSlice = createSlice({
  name: 'checkout',
  initialState: { orderId: null },
  reducers: { setOrderId: (state, action) => { state.orderId = action.payload } }
})

// checkout-app
dispatch(setOrderId('123'))

// host app
const orderId = useSelector(state => state.checkout.orderId)

3. API-based:

Micro-frontends communicate only through APIs, not shared state.

// checkout-app calls host API
await fetch('http://host-api/orders', { method: 'POST', body })

// host app receives and updates

Simplest for independent teams; slowest due to network round-trips.

Styling and Layout

CSS isolation: Each micro-frontend ships CSS. Risk: conflicts (both define .button).

Mitigations:

  • BEM naming: checkout__button, dashboard__button (verbose but safe).
  • CSS modules: Scopes CSS to component (requires build tool).
  • CSS-in-JS: Styles as objects, no conflicts.
  • Shadow DOM: Encapsulates styles (Web Components approach).

Layout coordination: How does the host position micro-frontends?

// host app
<div className="app">
  <header>Logo, nav (host)</header>
  <div className="micro-frontends">
    <div id="checkout-root"></div>
    <div id="dashboard-root"></div>
  </div>
</div>

Routing

Shared routing: Host app owns router, passes route to micro-frontend.

// host app (React Router)
<Routes>
  <Route path="/checkout/*" element={<CheckoutApp />} />
  <Route path="/dashboard/*" element={<DashboardApp />} />
</Routes>

Independent routing: Each micro-frontend owns its routes. Host only shows one at a time.

Second approach is simpler: less coupling between host and micro-frontends.

Deployment Strategy

Continuous deployment: Each micro-frontend deploys independently.

  1. Checkout team commits code.
  2. CI/CD pipeline builds and uploads checkout@http://prod-cdn.com/remoteEntry.js.
  3. Host app automatically picks up new version (versioned remote entry).

Risk: breaking changes in micro-frontend API. Mitigate:

// Host imports specific version
remotes: {
  checkout: 'checkout@http://prod-cdn.com/remoteEntry.v2.js'
}

// Micro-frontend maintains backward compatibility

Common Pitfalls

Bundle bloat: Each micro-frontend might bundle React separately. Mitigate with shared dependencies:

shared: {
  react: { singleton: true, eager: true },
  'react-dom': { singleton: true, eager: true },
}

Cascading failures: If one micro-frontend goes down, host shouldn't crash.

async function loadMicroFrontend(name, url) {
  try {
    return await import(url)
  } catch (e) {
    console.error(`Failed to load ${name}:`, e)
    return <ErrorBoundary>Failed to load {name}</ErrorBoundary>
  }
}

Performance: Each micro-frontend requires a network request. Lazy-load non-critical micro-frontends:

<Suspense fallback={<Loading />}>
  {showCheckout && <CheckoutApp />}
</Suspense>

When to Adopt

Micro-frontends are worth the complexity when:

  • Multiple independent teams building features.
  • Frequent deployments required.
  • Scaling engineering org (50+ engineers).

For small teams or monoliths, they add overhead without benefit.

Frequently asked questions

What's the difference between Module Federation and Web Components?

Module Federation (Webpack) shares dependencies at build time, lightweight. Web Components are browser standard, framework-agnostic but heavier.

How do micro-frontends communicate?

Events (CustomEvent), shared state store (Redux), or API calls. Event-based is loosely coupled. Shared store is tighter coupling but faster.

When should I use micro-frontends?

When you have 50+ engineers in independent teams, each owning features end-to-end. For smaller teams, a monolith is simpler.

Let's build your next idea

One conversation to scope the work, meet the team, and get a proposal — usually within two business days.