nested routes, <Outlet> & route context

[check: …]
📖 Read the full guide — nested routes, <Outlet/>, createRootRouteWithContext, and context inheritance down the match chain. This page is the interactive companion.

The one idea

Routes nest like Russian dolls: a parent renders <Outlet/>, the matched child fills it. And context set at the root flows DOWN to every descendant through createRootRouteWithContext — no prop-drilling across the tree. Each route may augment the context in beforeLoad, and the union is inherited by all its children.

Two mechanisms, one route tree. Nesting is about rendering: <Outlet/> renders exactly one matched child (or null if there is none). Context is about data: the router carries a context object down the match chain; a route reads the merged set via Route.useRouteContext(). The two are independent but compose — a deeply nested leaf renders inside every ancestor's <Outlet/> and reads every ancestor's context.

parent renders <Outlet/> child fills it root context → all descendants beforeLoad augments, children inherit Route.useRouteContext() reads the union

The nesting + context visualizer — click a route, see its render chain & context

Left: the curated src/routes/ tree (6 routes). Right: type a URL (or click a sample) — the resolver shows the component render chain with <Outlet/> markers, then the context each level can read. Notice the root context key queryClient reaches every leaf, while fetchPosts (added in posts's beforeLoad) only appears under /posts — never on the / sibling branch.

__root (context origin) static / index $param beforeLoad adds context

The render tree — <Outlet/> is where the matched child goes

Every layout in the chain renders exactly one <Outlet/> — the slot the next matched route fills. A chain of N routes has N−1 outlets (the leaf has none). If a route defines no component, the router renders an <Outlet/> automatically.

Resolve a URL above to see its chain render here.

Context flow — set at the root, inherited DOWN, augmented per route

The root context (queryClient) is provided at createRouter({ context }) and typed with createRootRouteWithContext. Each route's beforeLoad may return an object that is merged in; the union is available to that route's loader, its component (via useRouteContext()), and every child. gold = root context, cyan = added at this level, grey = inherited from an ancestor.

Resolve a URL to see the context each matched route reads.

Concept → what it does → example

ConceptDoesExample
<Outlet/> Renders the one next matched child route (or null). Takes no props. <div><h1/> <Outlet/> </div>
nested route A child route whose URL extends the parent's; the parent wraps it through <Outlet/>. posts/$postId.tsx renders inside posts.tsx
createRootRouteWithContext<T>()() Creates the root route AND constrains the typed root context T. Double-called factory. createRootRouteWithContext<{queryClient}>()()
createRouter({ context }) Provides the initial root context values (must satisfy T). createRouter({ routeTree, context:{ queryClient } })
beforeLoad Runs serially, per-navigation, before the loader. May return an object merged into this route's context (inherited by children). beforeLoad: () => ({ fetchPosts })
Route.useRouteContext() Reads the merged context (root + every ancestor's beforeLoad) inside a route component. const { queryClient } = Route.useRouteContext()
context inheritance Context merges DOWN the match chain: each child sees the union of all ancestors' context. leaf sees queryClient + fetchPosts + postMeta
no component If a route omits component, the router renders an <Outlet/> for it automatically. a layout route that only wraps, renders nothing of its own
Verified against tanstack.com/router · outlets + /guide/router-context + /guide/data-loading (primary) and TkDodo — Context Inheritance in TanStack Router (secondary). Cross-refs: 🔗 file_based_routing (this bundle extends its nesting chain with context flow) · 🔗 router_type_safety · 🔗 path_search_params.