Return Blog
Architecture

Micro Frontends with Angular and React

Sergio Rojas
Sergio Rojas
4 min read 18 May, 2026
Share
Micro Frontends with Angular and React
Summarize with AI:
Prompt copied! Paste it (Cmd/Ctrl+V) in the chat. Open AI

Let me say something that’ll get me side-eye in certain architecture meetings: micro frontends are the most over-recommended pattern of the decade. For a solo project or a tight five-person team, they add ceremony you’ll regret. But the moment you have several teams shipping against the same product, on different stacks, with different release cadences, they stop being a buzzword and become the only thing keeping the codebase sane.

The clearest case for them looks like this: a business wants new features built in React without freezing an existing Angular platform, and each team needs to deploy on its own schedule without waiting on a giant coordinated release. That’s the real problem micro frontends solve, and it’s an organizational one disguised as a technical one.

When micro frontends actually earn their keep

Before any config, be honest about the trigger. You don’t reach for this because the tech is interesting. You reach for it when team autonomy is the bottleneck: independent teams, independent deploys, and a mix of legacy and modern stacks you need to evolve gradually instead of in one terrifying big-bang rewrite.

If a single team owns the whole frontend and ships together, a well-structured monorepo with clear module boundaries will serve you far better. Micro frontends are for organizational scale, not for showing off.

The mental model: one shell, many remotes

The architecture is simpler than the hype suggests. You have one shell (the host) that owns the things everyone shares: routing, layout, authentication, and the global shell of the page. Then you have remotes, which are independently built and deployed applications mounted into the shell at runtime.

The runtime glue is Module Federation. In the modern Angular world you’ll often see Native Federation built on esbuild, but the concept is identical: the shell pulls remote code over the network at runtime, instead of bundling everything together at build time.

Making Angular and React coexist

Here’s the trick that makes a mixed stack manageable: stop thinking about frameworks and start thinking about contracts. Every remote, whether it’s Angular or React inside, exposes the exact same thing to the shell, a mount(element, props) and an unmount(). The shell never needs to know what’s behind that door.

A React remote exposing that contract looks like this:

// product-reviews/src/bootstrap.tsx
import { createRoot, Root } from "react-dom/client";
import App from "./App";
 
let root: Root | null = null;
 
// A framework-agnostic contract the shell can call, no matter what's inside.
export function mount(el: HTMLElement, props: Record<string, unknown>) {
  root = createRoot(el);
  root.render(<App {...props} />);
}
 
export function unmount() {
  root?.unmount();
  root = null;
}

And the Angular shell mounts it through a thin wrapper component, never bundling a line of React itself:

@Component({
  selector: "app-react-island",
  template: `<div #host></div>`,
})
export class ReactIslandComponent implements AfterViewInit, OnDestroy {
  @ViewChild("host") host!: ElementRef<HTMLElement>;
  private remote?: { unmount: () => void };
 
  async ngAfterViewInit() {
    // Pulled over the network at runtime, decoupled from the shell's build.
    const { mount, unmount } = await loadRemoteModule({
      remoteName: "productReviews",
      exposedModule: "./mount",
    });
    mount(this.host.nativeElement, { productId: "SKU-123" });
    this.remote = { unmount };
  }
 
  ngOnDestroy() {
    this.remote?.unmount(); // clean teardown matters, or you'll leak
  }
}

That’s the whole interop story. Two frameworks, one boundary, zero tight coupling.

The traps nobody warns you about

The demo always works. Production is where the sharp edges live:

  • Shared dependency hell:

The fastest way to ship a 4 MB bundle is to let every remote pull its own copy of React or Zone.js. Mark heavy shared libraries as singletons in your federation config, and pin compatible versions deliberately.

  • Style leakage:

Global CSS from one remote will happily repaint another. Isolate aggressively, whether that’s Shadow DOM, strict scoping, or a disciplined design-system-only rule for global styles.

  • Cross-app communication:

Resist the urge to share a global store across remotes. That recreates the exact coupling you were trying to escape. Pass data down through props and bubble events up through a thin, well-defined event bus.

Best practices from production

  • Treat the design system as a contract:

A shared component library keeps independently-built remotes from looking like four different products stitched together.

  • Version the contract, not the internals:

Teams should be free to refactor everything inside a remote, as long as the mount/unmount surface stays stable.

  • Give every remote its own CI/CD:

Independent deployment is the entire point. If one remote’s release blocks another, you’ve built a distributed monolith.

Wrapping up

Micro frontends are a powerful answer to a specific question: how do many teams ship one product without stepping on each other? When that’s genuinely your problem, the runtime composition, the contracts, and the independent pipelines pay for themselves quickly.

But if you can solve it with a clean monorepo and good module boundaries, do that instead. The best architecture is always the simplest one that lets your teams move fast, and the senior skill is knowing which problem you actually have.