Skip to main content
shivam gairola..

OPEN TO SELECT PROJECTS

OPEN TO SELECT PROJECTS

OPEN TO SELECT PROJECTS

OPEN TO SELECT PROJECTS

Frontend

Micro Frontends: When They Are Worth It and When They Are Not

Micro frontends solve an organizational problem, not a technical one. A field guide to the integration patterns, the real costs, and how to know if you actually need them.

Jul 2, 20265 min read

Micro frontends get pitched as a scaling technique, and that framing gets teams into trouble. They do not make your app faster or your code cleaner. What they do is let independent teams build, test, and deploy parts of a single product without coordinating every release. That is an organizational win, and it comes at a real technical cost.

So the first question is never "how do we build micro frontends". It is "do we have the problem micro frontends solve". If you have one team of six people, you almost certainly do not, and a well-structured monolith will serve you far better. If you have eight teams stepping on each other in one repo, blocked on each other's releases, then it is worth the conversation.

Here is what I have learned about doing it without creating a distributed monolith, which is the worst of both worlds.

The problem they actually solve

A monolithic frontend forces one deploy pipeline, one dependency version set, and one release cadence on everyone. That is fine until the number of teams grows. Then you get:

  • Merge queues and release trains where one team's bug blocks everyone.
  • A shared dependency upgrade that requires all teams to coordinate at once.
  • A codebase so large that build and test times punish every contributor.

Micro frontends cut the product into independently deployable pieces, each owned end to end by a team. Team A ships checkout on Tuesday, team B ships search on Thursday, and neither waits on the other.

                +----------------------+
                |   Container / Shell        |   routing, shared shell, auth
                 +----------+----------+
                                       |
        +---------------+------------------+
        |                              |                                    |
   +---------+        +-----------+        +---------+
   | Search    |          |Checkout   |        | Account  |
   | Team A    |          | Team B       |        | Team C     |
   +---------+        +-----------+        +---------+
   own repo,          own repo,          own repo,
   own deploy         own deploy         own deploy

Each box owns its code, tests, and release. The shell composes them into one product for the user.

The integration patterns, from simple to complex

There is a spectrum here, and picking the simplest one that meets your need is the whole skill.

Route-based composition (start here). Different routes are served by different apps behind a reverse proxy or edge router. /search goes to one app, /checkout to another. There is a full page load at the boundary, which is a real downside, but the isolation is total and the setup is trivial. For many products this is genuinely enough.

Build-time integration. Each micro frontend is published as a versioned package and the container composes them at build time. The catch is that a container rebuild and redeploy is needed to pick up a new version, which quietly reintroduces the coupling you were trying to escape. I generally avoid this one, it looks like independence but is not.

Runtime integration via Module Federation. The container loads micro frontends at runtime, so each can deploy independently and the user gets a single-page-app experience with no full reloads at boundaries. This is the pattern most people actually mean, and Webpack or Rspack Module Federation is the common tool. It is powerful and it is where most of the hidden cost lives.

Web Components as the wrapper. Each micro frontend exposes itself as a custom element, which gives framework-agnostic boundaries and native style isolation via the shadow DOM. Useful when teams genuinely use different frameworks, though that itself is a smell worth questioning.

The costs nobody puts on the slide

Shared dependencies are the hard part. If three micro frontends each ship their own copy of React, your users download React three times. Module Federation lets you share singletons, but now you have a version negotiation problem: what happens when team A wants React 19 and team B is stuck on 18. This is the single biggest source of pain, and it needs an explicit governance decision, not an ad hoc one.

Consistency erodes. Independent teams drift. Buttons stop matching, spacing gets inconsistent, and the product starts to feel like several products in a trench coat. The only real defense is a shared design system, published as a versioned package and treated as a genuine contract, not a suggestion.

Performance regresses if you are careless. Multiple frameworks, duplicated dependencies, and separately loaded bundles add up. Without a shared runtime and disciplined code splitting, a micro frontend architecture can easily be slower than the monolith it replaced.

Local development gets harder. A developer on the checkout team may need the shell and a couple of neighbors running to see their work in context. This needs investment: good mocking, the ability to run one micro frontend against a deployed version of the rest, and fast local startup.

How to keep it from becoming a distributed monolith

The failure mode is building all the operational complexity of a distributed system while keeping all the coupling of a monolith. You avoid it with a few hard rules:

  • Independent deploys are non-negotiable. If shipping one micro frontend requires redeploying another, you do not have micro frontends. This is the litmus test.
  • Communicate through contracts, not shared internals. Cross-app communication goes through well-defined events or a small shared API, never by reaching into another team's components or store.
  • One team owns each piece end to end. Ownership that spans teams recreates the coordination problem you were escaping.
  • Centralize the boring cross-cutting concerns. Auth, routing, and the design system live in the shell or a shared package. Do not let every team reinvent them.

My honest default

For most teams and most products, the answer is do not. A modular monolith with strong internal boundaries, the kind where features are cleanly separated and dependencies point one direction, gives you most of the maintainability benefit with none of the distributed-system tax. You can always split later, and splitting a clean monolith is far easier than un-splitting a bad micro frontend setup.

Reach for micro frontends when the organizational pain is real and measured: multiple teams, genuinely blocked on each other, with the platform investment to support independent deploys. When that is true, they are the right tool. When it is not, they are an expensive way to make a simple problem complicated.

Building something in this space?

I take on select builds when the work is worth doing right.

Start a conversation