Share core libraries as singletons, keep contracts between apps small and typed, and isolate each remote so its failure cannot break the page. A remote is a micro-frontend that the shell (the host app) loads at runtime.
Shared dependencies: React, the router and the design system are declared as shared singletons with version ranges. Singleton means only one copy exists, and the host provides it. This matters for React, because two copies break hooks and context. A platform team owns the versions and upgrades all remotes on a schedule. Strict version checks catch mismatches in CI rather than in production.
Contracts: each remote exposes a small, typed entry point, like mount(el, props) or a component. It publishes its types. Apps communicate in three ways:
- through the URL
- through explicit props from the shell
- through a minimal event bus with documented events
I avoid a shared global store, because it couples the teams again.
Failures: remotes load at runtime, so any of them can fail. The shell:
- wraps each remote in an error boundary and a
Suspensefallback - times out slow loads
- shows a degraded state instead of a blank page
Remote manifests are versioned, so the shell can pin or roll back one remote.
Consistency:
- shared tokens
- a shared auth and telemetry SDK
- contract tests between the shell and the remotes
- Share React and the design system as singletons, so only one copy loads.
- A platform team owns the dependency versions.
- Keep contracts small and typed. Communicate through the URL, props or an event bus.
- Give each remote an error boundary and a timeout.
- Version the manifests so one remote can be rolled back.
- patterns.dev (https://www.patterns.dev/)patterns.dev