The problem shape
Reach for this when release coordination has become the bottleneck, not the code.
The signal is organizational rather than technical. Several teams work in one frontend. Any one of them shipping requires a release train everyone rides. A revert caused by one team blocks the other four. Estimates inflate because half of every task is coordination. The codebase may be perfectly reasonable and it still takes three weeks to ship a two-day change.
The mistake is treating that as a code problem and reaching for a rewrite. The code is rarely what stopped you. The deployment unit is.
Worth being precise about the actual goal: microfrontends buy independent deployability. They do not make the code cleaner, the bundle smaller, or the architecture inherently better. Adopting them for any reason other than deploy independence means paying the cost without collecting the benefit.
The structure
Split along ownership and workflow, which usually means splitting along the business capabilities the product already has. In an accounts receivable product that meant invoicing, payments, and customer management: three surfaces, each owned end to end by one team, each independently deployable.
Each surface owns its own routes, its own data fetching, its own release cadence, and its own decision about when to ship.
Between surfaces there are exactly three sanctioned forms of contact. A shell owns routing, authentication, and layout, and knows nothing about what lives inside each surface. Shared contracts cover cross-cutting concerns such as the session, the design system, and analytics, and are versioned like any published dependency. Navigation happens by URL, so one surface hands off to another without importing it.
Everything else is forbidden by construction. A surface must not import another surface's internals, read its state, or depend on its release timing. That rule is what preserves the only property you are buying.
The split that fails is the one drawn along technical layers, a data microfrontend and a UI microfrontend, or drawn per page. Both produce boundaries that every feature has to cross, so every change touches multiple deploy units and coordination comes straight back with extra ceremony attached.
Trade-offs
Bundle duplication is real. Shared dependencies get shipped more than once unless you invest in module federation or externals, and that investment is ongoing rather than one-time.
Cross-surface changes get harder, on purpose. A change spanning invoicing and payments now spans two deploys and possibly two teams. That friction is the boundary working as intended, and it is genuinely painful when the boundary was drawn in the wrong place.
Local development gets more complicated. Engineers need a story for running one surface against the others without booting everything.
Operational surface multiplies. More pipelines, more environments, more places a deploy can fail, and a versioning story for the shared contracts.
Consistency requires active effort. Independent teams drift. Without a shared design system that is genuinely adopted, the product starts to look like several products.
When not to reach for it
One team. With a single team there is nobody to coordinate with, so there is no bottleneck to remove. You would pay every cost and collect nothing.
When the real problem is slow CI or a slow build. Those look like release pain and are far cheaper to fix directly. Splitting the deploy unit to work around a twenty minute pipeline is an expensive way to avoid a tractable problem.
When the domain has no natural seams. If every workflow touches every other, any boundary you draw will be crossed constantly, and you will have added coordination rather than removed it.
Before ownership is settled. The split encodes team boundaries into the architecture. Doing that while ownership is still shifting bakes in a structure you are about to outgrow, and boundaries are much harder to move than code.