Skip to content
Angular ng microfrontends 4 min read

Introduction to Micro Frontends

A micro frontend architecture applies the lessons of microservices to the browser: instead of one monolithic single-page application, you split the UI into several independently developed, tested, and deployed pieces — each owned end-to-end by a separate team. The user still sees one cohesive app, but behind the scenes a thin host (the shell) stitches together fragments (the remotes) that load at runtime. For a large Angular product with dozens of contributors, this is often the difference between a fast, autonomous organisation and a release train everyone is stuck waiting on.

The problem with the frontend monolith

A single Angular app starts out fine. As it grows past a few hundred components and a handful of teams, the cracks show:

  • Coupled releases. One team’s half-finished feature blocks everyone else’s deploy because there is a single build and a single bundle.
  • Slow builds. A full ng build of a large workspace can take many minutes, and CI runs it on every change.
  • Unclear ownership. Shared node_modules, shared routing, and shared global state mean a change in one corner can break a distant one.
  • One-size-fits-all upgrades. Bumping Angular or a UI library forces every team to migrate at the same time.

Micro frontends address these by drawing hard boundaries: each remote is its own deployable unit with its own pipeline, its own bundle, and — within reason — its own upgrade cadence.

How it fits together

A typical Angular setup has one shell application and several remote applications. The shell owns the top-level shell concerns — the global layout, the header, authentication, and the root router. Each remote exposes one or more lazily loaded routes or components that the shell pulls in at runtime, not at build time.

                ┌─────────────────────────────┐
                │           Shell             │
                │  (header, auth, root router)│
                └───────────────┬─────────────┘
        loads at runtime        │
   ┌────────────────┬───────────┴───────────┬────────────────┐
   ▼                ▼                        ▼                ▼
┌────────┐   ┌─────────────┐         ┌─────────────┐   ┌──────────┐
│ Catalog│   │   Checkout  │         │   Account   │   │  Support │
│ (team A)│   │  (team B)   │         │  (team C)   │   │ (team D) │
└────────┘   └─────────────┘         └─────────────┘   └──────────┘

The runtime-loading mechanism is what distinguishes micro frontends from ordinary lazy loading. With Module Federation (or its Angular-friendly successor, Native Federation), the shell fetches a remote’s remoteEntry.js, resolves its exposed modules, and renders them as if they were always part of the app.

In a modern standalone Angular shell, a federated remote is wired into the router with loadRemoteModule:

import { Routes } from '@angular/router';
import { loadRemoteModule } from '@angular-architects/native-federation';

export const routes: Routes = [
  {
    path: 'checkout',
    loadComponent: () =>
      loadRemoteModule('checkout', './CheckoutComponent').then(
        (m) => m.CheckoutComponent,
      ),
  },
];

When a user navigates to /checkout, the shell downloads the checkout remote on demand. Until then, none of that team’s code is in the bundle.

Composition strategies

There is more than one way to combine fragments. Pick the one that matches your team boundaries and runtime constraints.

StrategyWhere composition happensBest for
Build-time (libraries)Compile time, one bundleShared design system, not true independence
Run-time (Module/Native Federation)Browser, on navigationIndependent deploys within one SPA
Server-side / edge includesServer or CDNMixed tech stacks, SEO-heavy pages
iframesBrowser, full isolationHard sandboxing, legacy embeds

For Angular teams that want independent deploys while keeping a single, seamless SPA experience, run-time federation is the default choice — it shares the Angular runtime and routing without giving up autonomy.

What you gain — and the costs

The benefits are organisational as much as technical:

  • Independent deployment. Team B ships checkout on Tuesday without touching team A’s catalog.
  • Team autonomy. Clear ownership of code, pipeline, and on-call.
  • Incremental upgrades. Remotes can adopt a new Angular version one at a time, behind a shared-dependency contract.
  • Faster, smaller builds. Each pipeline builds only its own slice.

These do not come for free. You now operate several pipelines instead of one, you must coordinate the versions of shared singletons (Angular, RxJS, your design system), and you can accidentally ship the framework multiple times if dependency sharing is misconfigured. Cross-remote communication and shared state also need a deliberate design rather than a global service.

Warning: Micro frontends are an answer to an organisational scaling problem, not a default architecture. If one or two teams own the whole app, a well-structured Nx monorepo with lazy-loaded feature libraries gives you most of the build benefits with none of the runtime complexity. Reach for micro frontends when team boundaries — not file counts — are the bottleneck.

Best practices

  • Adopt micro frontends only when independent team deployment is a real, felt need — otherwise prefer a monorepo with lazy feature libraries.
  • Keep the shell thin: it should own layout, auth, and routing, and stay out of feature logic.
  • Treat shared dependencies (Angular, RxJS, your UI kit) as a versioned contract, and share them as singletons to avoid loading the framework twice.
  • Define explicit, narrow contracts between shell and remotes — exposed components, route paths, and a small event/state surface — rather than reaching into each other’s internals.
  • Prefer Native Federation for new Angular work; it tracks the modern build tooling more closely than the original webpack Module Federation plugin.
  • Make each remote runnable in isolation so teams can develop and test without standing up the whole platform.
  • Monitor bundle composition in CI to catch accidental duplication of shared libraries early.
Last updated June 14, 2026
Was this helpful?