Micro-Frontends Document Shell
micro-frontends document shell micro-frontend architecture lives or dies on the document shell: unified head po micro-frontends split a product across
Introduction
Micro-frontends split a product across autonomous teams, each shipping UI to a shared document shell — the single HTML document owning <head>, global nav, CSP, and mount points for remotes. Shell HTML quality determines SEO, security, and whether users perceive one product or a franken-page.
Business problem
Business pressure: Enterprise acquisitions integrate three codebases on one domain. Without a shell contract, each team injects duplicate GTM snippets, conflicting meta tags, and incompatible design tokens — brand trust erodes and SEO duplicates explode.
- Velocity: Teams deploy independently — shell must version mount points and shared assets safely.
- SEO: One canonical, one title policy — remotes cannot each emit full documents.
- Security: Single CSP and SRI policy at shell — remotes run inside trust boundaries.
Why this feature exists
Platform motivation: Monolith frontends bottleneck 20 teams. Micro-frontends trade integration complexity for autonomy. The shell exists because browsers have one document — you cannot merge three <html> tags.
- History: iframes (Amazon), module federation (Webpack 5), single-spa, import maps, web components.
- Alternative rejected: Separate subdomains per team — breaks unified checkout and cookie session.
- Modern role: Shell as platform product; remotes as consumers of HTML slots and event bus.
Browser internals
Inside the engine: Shell HTML loads first. Parser builds DOM with empty mount nodes. Remote scripts (federation or custom elements) fetch asynchronously and attach subtrees — layout recalc when remotes paint can cause CLS if shell lacks skeleton dimensions.
- Script order: Shell defines import map / remoteEntry; remotes must not document.write.
- CSS: Global tokens in shell; remote CSS may FOUC — shadow DOM or strict naming conventions.
- Focus: Cross-remote focus management is shell responsibility for a11y.
Rendering workflow
Rendering path: Edge serves shell HTML with SSR optional for shell nav. Remotes hydrate client-side or SSR fragments injected server-side (composition layer). Composition at edge (ESI, workers) vs client (federation) affects TTFB and cache.
- Client composition: Faster shell TTFB; CLS risk when remotes load.
- Server composition: Heavier origin; better first paint coherence.
- Navigation: Shell router vs remote routers — must not fight History API.
Feature deep dive
Shell contract — slots, shared dependencies, event API, and forbidden head mutations. Document in HTML comments or meta for tooling.
- Slots:
<div id="mfe-checkout">with min-height skeleton. - Shared libs: React singleton via federation shared scope — version pinned in shell.
- Events: CustomEvent bus on window — decouple remotes without importing each other.
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Acme — Shell</title><meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://cdn.acme.com"><link rel="stylesheet" href="https://cdn.acme.com/shell/design-system.css"></head><body><header id="shell-nav"><!-- shell team --></header><main><div id="mfe-catalog" data-min-height="480"><!-- remote A --></div><div id="mfe-cart" data-min-height="120"><!-- remote B --></div></main><script type="importmap">{"imports":{"react":"https://cdn.acme.com/react@18.3.1"}}</script><script type="module" src="https://cdn.acme.com/shell/bootstrap.js"></script></body></html>
Accessibility analysis
A11y architecture: One <main>, one skip link, logical heading order across remotes — enforced by shell lint. Remotes must not emit landmark roles that duplicate shell landmarks.
- Heading hierarchy: Shell owns h1 policy per route — remotes start at h2.
- Focus: Shell announces route changes; remotes register with focus registry.
SEO impact
SEO architecture: Shell sets canonical, hreflang, and robots. Remotes supply fragment HTML for main content — composition service merges before crawl. Client-only remote mount fails SEO for public routes.
- SSR merge: Server includes remote HTML for Tier-0 URLs in shell main slot.
- Title: Shell template + remote meta fragment — single title element.
Security considerations
Security boundary: Remotes are semi-trusted. CSP at shell restricts script origins. Avoid remotes injecting inline script — use nonce from shell SSR.
- CSP nonce: Generated per request in shell — passed to trusted remotes only.
- iframe sandbox: Untrusted third-party MFE in sandboxed iframe with postMessage API.
Performance impact
Performance: Sequential remote loading hurts LCP — parallelize with modulepreload for remoteEntry. Reserve slot heights in shell HTML to prevent CLS.
- LCP: LCP element often in catalog remote — preload its critical CSS in shell head.
- INP: Multiple React roots — avoid duplicate scheduler work; shared runtime.
Real production example
Production pattern: Zalando-style shell with server-side fragment inclusion for PLP HTML; checkout remote federated client-side behind auth. Shell CI validates merged HTML with axe and canonical uniqueness.
- Composition service: Node worker fetches remote SSR fragments with timeout fallback skeleton.
- Deploy: Shell daily; remotes hourly — compatibility matrix in registry.
Enterprise usage
Enterprise: Platform team owns shell repo, design tokens, CSP, and MFE registry. Product teams register mount points and semver contracts.
- ADR: Required for new remote — SEO tier, auth, and data classification.
- Observability: Shell RUM tags remote version on each slot via data attributes.
Common production failures
What breaks in prod: Remote deploy breaks shared React version — white screen. Two remotes set document.title — SEO chaos. Missing skeleton height — CLS 0.35 on mobile.
- Incident: Federation shared dependency mismatch — rollback remote only; shell pins versions afterward.
- SEO: Client-only catalog remote — Google indexed shell nav only; added SSR fragment merge.
Architecture review questions
- Who owns head tags, CSP, and canonical for this route?
- Is Tier-0 content present in merged server HTML?
- What happens when a remote fails to load — fallback HTML?
- How are shared library versions pinned and tested?
- Is there exactly one main landmark and one h1?
Hands-on project
Project: Build minimal shell with two mount points and mock remotes (web components). Add CSP, skeleton min-heights, and merged HTML snapshot test.
- Deliverable: shell.html, two remote scripts, composition diagram.
Interview questions
How do micro-frontends affect SEO and what mitigations do you use?(Advanced)
Public routes need server-composed HTML in shell main — not client-only mounts. Shell owns canonical and structured data merge. Use SSR fragments or edge includes for Tier-0 content.
Follow-up: When is iframe-based MFE acceptable?
Try it yourself
Edit the HTML, CSS, or JS panels — the preview updates as you type.
Try it yourself
Summary
Micro-frontend architecture lives or dies on the document shell: unified head policy, accessible landmarks, CSP, and server composition for SEO routes. Platform teams treat the shell as a product with contracts, not a throwaway wrapper.