-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Feature: Lazy route tree registration for Module Federation micro frontends #7080
Description
Problem
When using TanStack Router with Module Federation micro frontends (MFEs), the entire route tree must be constructed before createRouter() is called. This means all remote MFE route trees must be fetched and registered upfront at application startup, even if the user will only visit one or two of them during their session.
In our case, we have 13 MFE remotes. We use route.update() and route.addChildren() to inject each remote's route subtree into placeholder routes, then call createRouter(). This forces us to load all 13 remotes at startup via Promise.allSettled, which:
- Increases initial load time — every MFE manifest and entry point must be fetched before the app becomes interactive
- Wastes bandwidth — users typically only access 1-2 MFEs per session
- Creates a single point of failure — a slow or unavailable MFE delays the entire application boot (we mitigate with
Promise.allSettled+ fallbacks, but it's still suboptimal)
Current workaround
// We wrap everything in a lazy() to defer, but ALL remotes still load together
const AppWithRoutes = lazy(async () => {
// All 13 MFEs must be registered before createRouter()
await Promise.allSettled([
registerSubtree(DashboardRoute, () => loadRemote('dashboard/routeTree')),
registerSubtree(ProductRoute, () => loadRemote('product/routeTree')),
// ... 11 more remotes
])
const router = createRouter({ routeTree })
return { default: () => <RouterProvider router={router} /> }
})Where registerSubtree does:
async function registerSubtree(baseRoute, subtreeLoader) {
const { routeTree: subtree } = await subtreeLoader()
baseRoute.update({
component: subtree.options.component,
})
subtree.children.forEach(route => {
route.update({ getParentRoute: () => baseRoute })
})
baseRoute.addChildren(subtree.children)
}Proposal
It would be great if TanStack Router supported lazy route tree registration — the ability to dynamically add children to a route at navigation time rather than only at startup.
One possible approach: allow beforeLoad to register child routes dynamically. Something like:
// Ideal API — each MFE loads only when its route is first visited
const dashboardRoute = createRoute({
getParentRoute: () => appRoute,
path: 'dashboard',
beforeLoad: async ({ addChildren }) => {
const { routeTree } = await loadRemote('dashboard/routeTree')
addChildren(routeTree.children) // Dynamically register children
},
})This way:
- Each MFE's route tree is fetched only when the user navigates to that section
- The router can still be created immediately with just the placeholder routes
- Failed MFE loads only affect the specific route, not the entire app startup
Context
This is a common challenge in Module Federation architectures where the host application doesn't know the remote's route structure ahead of time. The current requirement to have the full route tree before createRouter() makes it difficult to achieve true per-route lazy loading of MFE bundles.
Environment
@tanstack/react-router(file-based routing with Vite plugin)- Module Federation via
@module-federation/enhanced/runtime - 13 remote MFEs, each exposing a
routeTree