Nested & Context — the outlet + beforeLoad model

[check: …]
📖 guide (.md) ← react deep dive
📖 Pair this live React playground with the companion guide (.md) — this page is the rendered ground truth. ↗ Builds on frontend/tanstack-start: Nested & Context — the basics of <Outlet> + context. This bundle goes to the full hierarchy and context accumulation mechanism: the ancestor chain, how beforeLoad merges top-down, and parent→child loader dependencies.

a match is a chain, not a single component

When TanStack Router matches /admin/users/42, it does not render one component. It resolves the leaf (UserDetail) and walks its ancestor chain: [Root, Admin, Users, UserDetail]. Each ancestor renders as a layout with an <Outlet/> placeholder, and the next route in the chain renders inside it — nested rendering. At the same time, each route's beforeLoad returns a context object that is merged down the chain, so the leaf receives the union of every ancestor's context.

routebeforeLoad providescomponent rendersinherited at this level
/ Root {'{ theme, user }'} header + <Outlet/> {'{ theme, user }'}
/admin {'{ adminMode }'} sidebar + <Outlet/> {'{ theme, user, adminMode }'}
/admin/users {'{ userFilter }'} UserList {'{ theme, user, adminMode, userFilter }'}
/admin/users/$id {'{ detailTab }'} UserDetail {'{ theme, user, adminMode, userFilter, detailTab }'}

The chain only includes ancestors of the matched leaf. Navigate to /admin and the chain collapses to [Root, Admin] — Users and UserDetail never mount, so userFilter/detailTab are absent from context.

1 · the nested-context visualizer (edit me)

2 · Babel compiles JSX → element tree

<NestedContextDemo/> becomes React.createElement(NestedContextDemo). Clicking a route calls setActivePath → re-render → findLeaf + buildChain + accumulateContext rebuild the chain and merged context from scratch.

// (hit "compile & render" to see Babel's output)

3 · live React (nested chain + context accumulation, proven)

Click any route in the tree. The gold-check drives it directly: default /admin/users/42 → asserts chain [Root, Admin, Users, UserDetail] with all 5 context keys, then CLICKS /admin → asserts the chain collapses to [Root, Admin] and the context loses userFilter/detailTab. That proves matching, chain construction, and the top-down context merge all work.

routes: · gold: /admin/users/42→[Root,Admin,Users,UserDetail]{5 keys} → /admin→[Root,Admin]{3 keys}

intent → pattern

intentpatternwhy
wrap child routes in a layout parent component renders <Outlet/> the Outlet is where the matched child renders — that is nested rendering
provide data to a route + descendants beforeLoad: ({'{ context }'}) => ({'{ ...context, ... }}') spread inherited context then add your keys — returns the merged object
read parent data in a child loader loader: ({'{ context }'}) => fetchUsers(context.adminMode) context is already merged by the time the loader runs
opt OUT of nesting name the file _path.tsx (pathless layout) or un-nest via _ renders its own component without the parent layout / Outlet
access the accumulated context Route.useMatch().context / useLoaderData() type is the union of every ancestor's beforeLoad return type