Articles

Micro-Frontends Explained: When to Split a UI

Micro-frontends split one web app into independently deployable pieces owned by separate teams. How the pattern works, and its real tradeoffs.

Takina Takina · · 4 min read
A code editor showing modular UI components

A micro-frontend is an architectural pattern that splits a single web application into smaller, independently built and deployed pieces, each owned end-to-end by a separate team. It’s the frontend answer to microservices: instead of one team shipping one giant codebase, several teams ship separate UI fragments that get composed into one page at runtime or build time.

The pattern didn’t come from a technical need so much as an organizational one. Once a frontend team grows past a certain size, a single shared codebase becomes a bottleneck — deploys queue up, one team’s bug blocks another team’s release, and nobody wants to own the build pipeline. Micro-frontends trade some technical simplicity for team autonomy.

How composition actually works

There are a few common ways to stitch independently built fragments into one page:

  • Build-time integration. Each micro-frontend publishes a package; the shell app imports and bundles them together at build time. Simple and fast at runtime, but it re-couples deploys — the shell has to rebuild whenever a fragment changes, which undercuts the whole point.
  • Runtime integration via iframes. Each fragment lives in its own iframe. Strong isolation (styles and scripts can’t leak across the boundary) but painful for shared state, routing, and layout — iframes were never designed to feel like one seamless app.
  • Runtime integration via JavaScript. The shell loads each fragment’s JavaScript bundle dynamically and mounts it into a DOM node, often using dynamic import(). This is the most common approach today and the one most “micro-frontend frameworks” build on top of.
  • Server-side composition. The server assembles HTML fragments from different backends into one response, similar to old-school server-side includes. Good for content-heavy pages, harder to reconcile with client-side interactivity.

Most production setups end up as a hybrid: a thin shell application owns routing and shared layout, while route-level pages are lazy-loaded as separate bundles, conceptually similar to how a monorepo can share tooling without sharing a deploy pipeline. Whether the code sits in a monorepo or separate repos is a build-organization choice, orthogonal to whether the frontend itself is split at runtime.

Shared concerns that don’t split cleanly

The hard part of micro-frontends isn’t loading separate bundles — it’s the things that have to be consistent across them regardless:

  • Design system. Every fragment needs to look like it belongs to the same product. This usually means a shared component library or design tokens, versioned and consumed independently by each team — which reintroduces a coordination point the pattern was supposed to remove.
  • Shared state. A shopping cart, an auth session, a user preference — something usually needs to cross fragment boundaries. Custom events, a shared store, or Web Components with attribute-based communication are the typical answers, since none of them require the fragments to share a bundler.
  • Routing. If each fragment owns a slice of the URL space, the shell needs a routing layer that can hand off to the right fragment without a full page reload, which resembles how a backend for frontend layer routes requests to the right service on the server side.
  • Duplicate dependencies. Without care, five fragments each ship their own copy of a framework, and the user pays for it in bundle size. Shared, versioned externals or an API gateway-style edge layer that enforces a common runtime can keep this in check, though it adds its own coordination cost.

When it’s worth the cost

Micro-frontends solve an organizational problem, not a performance one — a well-factored single codebase with clear module boundaries and a fast CI pipeline outperforms micro-frontends on almost every technical axis: bundle size, latency, and simplicity all favor staying unified.

The pattern earns its keep when:

  • Multiple independent teams need to ship on their own schedule without blocking each other.
  • Different parts of the product have genuinely different technical needs — one team is building with React Server Components, another needs a legacy framework kept alive during a migration.
  • A large legacy application is being incrementally replaced, and a strangler-fig-style migration needs old and new code to coexist on the same page.

It’s a poor fit for small teams, greenfield products, or anything where the org chart doesn’t actually require independent deploys. The coordination overhead — shared design systems, cross-fragment communication, duplicate-dependency management — is a permanent tax, not a one-time setup cost.

The takeaway

Micro-frontends split a web app into independently deployable pieces so separate teams can ship without blocking each other, at the cost of duplicated dependencies, harder cross-fragment state management, and a design system that has to be enforced across team boundaries. It’s an organizational pattern wearing a technical costume — reach for it when team autonomy is the actual bottleneck, not because splitting code feels more modern than a well-organized monolith.

Takina Takina · · 4 min read

CSS field-sizing Property Explained

The CSS field-sizing property lets form controls like textareas grow to fit their content automatically, without JavaScript resize listeners.

#CSS #Web Development #Frontend