Micro-Frontends: Architecture, Composition and Trade-offs
Micro-frontends explained: splitting a web app by team, build-time vs runtime composition, module federation, shared dependencies and when to avoid them.
Micro-frontends split a large web application into smaller frontend pieces owned and deployed independently by different teams, then compose them into one experience for the user. A checkout team might own the cart and payment pages while a discovery team owns search and product pages. It's the frontend version of microservices, and it solves the same organisational problem, many teams blocked on one codebase and one release, at a similar cost in complexity.
When they make sense
- Many teams (often dozens of engineers or more) work on one product and step on each other in a single repository and release.
- Clear domain boundaries exist, such as search, product, cart, account, so pieces can be owned end to end.
- Independent deployment matters: teams need to ship on their own schedule without coordinating a global release.
- Gradual migration: replacing a legacy frontend piece by piece.
For one or two teams, micro-frontends usually cost more than they save. A well-structured monolith with clear modules and good CI is simpler and faster. See monolith vs microservices.
Composition approaches
| Approach | How | Pros | Cons |
|---|---|---|---|
| Route-based (separate apps) | Each team owns whole pages; links or a reverse proxy route between them | Strong isolation, simplest | Full page loads between areas; shared header and state duplicated |
| Build-time integration | Teams publish packages that a container app imports and builds | One optimised bundle, simple runtime | Not independently deployable: every change needs a container rebuild |
| Runtime integration (module federation) | The container loads remote modules from each team's deployed bundle at runtime | Independent deploys, shared dependencies | Version coupling at runtime, harder debugging |
| Server-side composition | A server or edge layer assembles HTML fragments from team services | Fast first paint, SEO-friendly | Infrastructure complexity, coordinated caching |
| Web components / iframes | Each piece is a custom element or iframe | Framework-agnostic, strong isolation (iframes) | Iframes hurt UX and accessibility; web components need careful styling and state |
The hard parts
- Consistent look and feel: a shared design system, distributed as versioned components and tokens, keeps pieces from diverging.
- Shared dependencies: loading three copies of a framework kills performance; share libraries as singletons with agreed version ranges, and budget bundle sizes. See Core Web Vitals.
- Communication between pieces: keep it minimal and explicit, through the URL, custom events or a tiny shared event bus, not a shared global store that couples teams again.
- Shared concerns: authentication, routing, analytics, error tracking and feature flags belong to the shell or shared libraries.
- Failure isolation: one micro-frontend failing to load should show a fallback in its area, not a blank page.
- Testing: contract tests between shell and pieces, plus end-to-end tests of critical journeys across them.
Ownership and governance
Micro-frontends work when each team owns a slice vertically: its UI, its backend-for-frontend or APIs, and its data. A platform or core team owns the shell, design system, build tooling and performance budgets, and sets a small number of rules every piece must follow. Without that, independence turns into inconsistency.
Where it’s used
Search, product, cart and account pages are owned and deployed by separate teams behind one shell.
Product teams contribute modules to a shared admin portal, loaded at runtime.
New micro-frontends replace parts of an old application route by route.
In an interview
- Justify micro-frontends by team scale and domain boundaries, or argue against them.
- Compare route-based, build-time, runtime and server-side composition.
- Address shared dependencies, design consistency and communication.
- Put shared concerns in the shell and isolate failures per piece.
Further reading
- Micro FrontendsCam Jackson, martinfowler.com
- Module Federation conceptswebpack documentation