Thiago Finch
All Writing
TanStackMicrofrontendsArchitectureReact

Why I Rewrote Our Microfrontend Shell in TanStack Start

Thiago Finch

The Problem We Were Solving

When I joined Nubank's web platform team in 2022, we had a classic microfrontend setup: a single shell application built with Create React App, federating six product domains via Webpack Module Federation. On paper, it was clean. In practice, every deployment was a negotiation.

Version pinning across six teams meant any major dependency update required coordinated releases. Our shell had become a bottleneck — the very thing microfrontends are supposed to eliminate.

The TanStack Start Proposal

TanStack Start's file-based routing and server function model map naturally to a microfrontend shell. Each route file can lazy-load a remote module while keeping type safety end-to-end — something that was essentially impossible with the old setup.

// Before: untyped dynamic import
const RemoteCheckout = React.lazy(() => import('checkout/App'))

// After: typed server function fetching route config
const routeConfig = await getRemoteRoutes({ domain: 'checkout' })

The shift to server functions also meant we could do route-level A/B testing without client-side conditionals cluttering every component.

What Actually Broke

Not everything was smooth. Our legacy analytics integration assumed window.__APP_SHELL__ to be set synchronously on load — an assumption that SSR immediately violated. We spent a week debugging phantom page views before tracing it to hydration timing.

The Outcome

Three months post-migration: deploy frequency up 2.4×, zero cross-team version conflicts in Q1 2026, and our Core Web Vitals LCP improved from 2.8s to 1.4s on a P75 mobile connection.

The architectural lesson: frameworks that align with your deployment model remove whole categories of problems. TanStack Start aligned with ours.