
Building a Microfrontend Platform on AWS
Great microfrontend platforms come from a handful of deliberate decisions, not a framework. What the best teams get right — and where designs still evolve.
Series: Microfrontends on AWS (2 articles)
- 1Building a Microfrontend Platform on AWS This article
A well-built microfrontend platform lets independent teams ship on their own cadence while your users see one seamless product. Getting there takes a few deliberate decisions — here they are.

Over the past few years I have helped teams move large frontends from monoliths to microfrontends on AWS, and I keep watching the same story play out. A frontend that ten teams share slows everyone down; one bad merge blocks everyone's release. So the organization splits it into independently deployed pieces, wins back its autonomy — and then discovers it has signed up for the distributed-systems complexity backend teams have wrestled with for a decade.
That trade is usually worth making. But whether you get a platform that scales or one that quietly becomes a distributed monolith — where you pay the full cost of running many services yet still can't deploy any of them alone — comes down to a handful of choices. Most have nothing to do with the framework you pick. This post is about the choices the best teams get right. A companion piece, A Microfrontend Platform Assessment , turns them into a checklist you can run against your own design. My hope is that together they save you a few of the detours I have watched teams take.
First, the moving parts
If you have built microservices, you already have the intuition: microfrontends apply the same idea to the browser. The UI is split into independently built, independently deployed pieces, then stitched back into one experience where the user never sees the seams. A few terms recur throughout, and they all fit into one picture:

The remotes are the pieces each team owns and ships on its own cadence. The shell is the thin host that frames and routes them — the picture frame, not the picture. Composition is how those pieces get assembled into one page. The manifest is the file that says which pieces to load and from where. And auth lives in one place that hands each remote only the access it needs. That single governed place — where the platform decides what may load and run — is the trust boundary.
The promise is simple: ten teams ship on ten schedules, and no one's release blocks another's. The catch is that you have just turned a frontend into a distributed system, and distributed systems fail in ways monoliths never had to.
What teams running this at scale have in common
Read enough public engineering write-ups from teams running microfrontends in production and the same decisions keep surfacing. That convergence is the interesting part: it tells you which choices are load-bearing and which are just taste.

The clearest fault line is where the pieces get stitched together — on the server or edge before the page arrives, or in the browser at runtime. That one choice ripples through everything else: SEO, first-paint performance, how much the shell has to do, and where the trust boundary naturally sits.
Composed on the server or at the edge, before the page reaches the browser, keeps first paint fast and pages crawlable. IKEA.com serves roughly 40 million static files through a CDN and shares no frameworks in the browser — its trust rule is refreshingly blunt: no shared globals. Zalando's Tailor composes per-team fragments on the server behind an explicit shared-dependency contract, and Cloudflare's dashboard routes URL paths to independently owned frontends through one Router at the edge — one place where traffic and trust are enforced.
Composed in the browser at runtime trades a little first-paint cost for maximum team independence, which suits behind-login apps where SEO doesn't matter. American Express's one-app loads modules at runtime from a server-owned module map — a governed manifest by another name. DAZN's "Bootstrap" loader owns the session and hands each remote a controlled interface instead of letting it reach for globals. Intuit's AppFabric gives each plugin a sandbox as its only route to shared capabilities, and Spotify's Backstage builds everything as plugins that talk through declared APIs, never a global store.
Strip away the framework differences and the same four patterns hold, whichever side of that line you land on:
- One governed place composes the page — the trust boundary — never scattered across remotes.
- No shared global store. Remotes talk through a narrow, brokered channel, so one team can't corrupt another's data.
- One component owns identity and hands down scoped access. No remote reimplements auth.
- The platform owns the manifest that decides what loads, rather than trusting whatever a remote serves.
One build, in practice

Here is one I worked on.
An AWS customer was moving a large product portfolio off an aging .NET monolith to React microfrontends. They sell access to several data products, each a rich website in its own right, and the products share a large common core — the shell, navigation, search, and data-visualization building blocks — while each also carries genuinely different functionality of its own. In platform terms each product is a tenant: it reuses the shared core and layers its specifics on top, and customers sign in to see only the products they pay for. Originally this all lived in one backend monolith with the common parts copy-pasted across products — the exact duplication the migration set out to end. I like it as a teaching case because the team got most of the four patterns right, and the parts they are still working through are just as instructive.
The heart of the rebuild was how to share that common core without copy-pasting it. The shared code now lives in an NX monorepo and the UI libraries ship as versioned NPM packages, so every product consumes the same components by dependency rather than by copy, while its distinct functionality lives in its own federated modules loaded alongside them. On that foundation, four decisions map cleanly onto the four patterns:
- The shell is the trust boundary. Composition is client-side — a React shell wires the remotes together in the browser via Module Federation while CloudFront serves the static assets. The shell owns authentication, login, and top-level routing; each remote handles only its own internal routing.
- The manifest is governed. Every file ships as an immutable, content-hashed chunk — the filename carries a hash of the contents, so it caches forever without going stale — behind one tiny entry manifest the platform controls. Ship a change and the next page load picks it up.
- Auth is centralized. A managed identity provider owns credentials, federation, and sessions, and a pre-token step stamps the user's entitlements into the signed token as it is minted — so the browser can't forge access it was never granted.
- Shared state is brokered. Modules talk over a TypeScript event bus, never a shared global store — chosen deliberately, so no remote can reach into another's data.
What I find more honest to talk about, though, is where the design is still moving — because no architecture is ever finished, and the compromises are where the real thinking lives. Three stood out when I reviewed it, each already carrying a mitigation.
**Tokens live in browser storage today**. It's the pragmatic starting point, but that storage is shared across every remote in the origin, so a single cross-site-scripting bug in any team's widget could lift the token and impersonate the user everywhere. The direction is to move custody into a shell-owned broker — a closure or service worker — so no remote ever touches the raw token. ¨
The remote entry, meanwhile, loads itself as trusted first-party code with no integrity check — simply what Module Federation does by default. A swapped or tampered file would run with full trust and nobody would notice, so the fix on the table is a signed manifest with a per-chunk hash the shell verifies before running anything.
None of these are exotic. They're the normal compromises you make to ship, then tighten as the platform matures — and each maps to a check in the companion assessment . That's the real lesson: a sound design isn't one with no open edges, it's one where the team knows exactly where the edges are and has a plan to close them.
The takeaway
The teams whose write-ups opened this post didn't get there by adopting a framework. They got there by making four decisions on purpose: composition in one governed place, brokered state instead of a shared store, centralized auth, and a governed manifest. Get those right and the framework — Module Federation, single-spa, native federation — becomes an implementation detail you can swap later.
The example shows why that discipline pays off: you can get most of the patterns right and still leave a production-breaking seam open, because those seams live in the gaps between the patterns. Which is also why "we chose framework X" is never an answer to "is this platform sound?"
If you want to pressure-test your own design, the companion post turns each pattern into a concrete question, maps it to the AWS service that implements it, and suggests where to start:
Further reading
- awslabs/frontend-discovery — a signed manifest / deployment schema for microfrontends
- Module Federation documentation —
mf-manifest.jsonand shared-dependency negotiation
About the author
Sylvain Leroy is a Migration and Modernization Solutions Architect at AWS with 20 years of experience across frontend, backend, and cloud architecture. He works with enterprise teams on distributed systems, application modernization, and secure platform design.
Series: Microfrontends on AWS (2 articles)
- 1Building a Microfrontend Platform on AWS This article
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article