Router Context

Purpose and Scope

Router context is the dependency-injection surface for a TanStack Router application. It lets you create the router with shared objects, then make those objects available to route lifecycle code and route-aware UI without reaching for global imports. In practice, this is where applications commonly place authentication state, query clients, feature flags, API clients, or request-specific values in server-rendered environments. The official Router positioning describes the route tree as the application contract; context is one of the values that participates in that contract alongside params, search schemas, loader data, links, and navigation.

The Router API index exposes the relevant building blocks for this flow: root route creation, router creation, route APIs, router hooks, route-context hooks, state hooks, and route-related types. That tells you where context fits in the public surface area even when an application uses file-based routing and generated route trees. You define the shape of shared context at the root, pass concrete values when creating the router, and consume the resolved route context from route code or framework hooks. Sources: docs/router/api/router.md

Relevant Source Files

  • docs/router/api/router.md - Lists the public Router API categories, including createRootRouteWithContext, createRouter, getRouteApi, useRouteContext, useRouter, useRouterState, useMatches, RouteOptions, RouterOptions, RouterState, and related route types that anchor context usage.

Core Primitives

The most important primitive for typed dependency injection is createRootRouteWithContext. It appears in the Router function index next to createRootRoute, which signals the distinction between an ordinary root route and a root route whose context type is declared up front. In an app, that root-level declaration becomes the type source for values passed into createRouter. The result is not just a runtime container; it is a compile-time agreement between the route tree, lifecycle hooks, loaders, navigation helpers, and UI hooks that read route data. Sources: docs/router/api/router.md

createRouter is the point where the route tree and its concrete runtime options come together. When context is used, the router is created with the generated or manually constructed route tree and a context object whose shape matches the root route’s declared type. This keeps dependency wiring explicit: the router owns the current application context, while routes can remain focused on route behavior. For example, an auth route can ask for context.auth in a guard, and a data route can use a shared client in a loader rather than importing a singleton.

const rootRoute = createRootRouteWithContext<{
  auth: AuthState
  queryClient: QueryClient
}>()({
  component: RootLayout,
})
 
const router = createRouter({
  routeTree,
  context: { auth, queryClient },
})

Route context and router context are related but not identical concepts. Router context is the object supplied when the router is created. Route context is the accumulated context visible at a particular route match after parent routes have had a chance to contribute or refine values. In a nested route tree, parent layouts can establish dependencies or derived values for their descendants, while child routes consume the final context that applies to their match. This matters because TanStack Router’s model is explicitly tree-based: parents, layouts, loaders, boundaries, and children compose into the route contract rather than operating as isolated screens.

System-to-Code Mapping

The API index shows context-adjacent functions, components, hooks, and types as part of one Router reference rather than as separate subsystems. Functions such as createRootRouteWithContext, createRoute, createFileRoute, createRouter, and getRouteApi belong to the route-definition and router-construction side. Hooks such as useRouteContext, useRouter, useRouterState, useMatches, useMatch, useParentMatches, and useChildMatches belong to the consumption side. Types such as RouteOptions, RouterOptions, RouterState, Route, RouteApi, and RouteMatch describe the contracts that connect these runtime operations. Sources: docs/router/api/router.md

ConcernPublic API namesHow it participates in context
Declare context typecreateRootRouteWithContextEstablishes the root context shape for the route tree.
Construct routercreateRouter, RouterOptionsSupplies concrete context values with the route tree and router options.
Define routescreateRoute, createFileRoute, RouteOptionsLets route lifecycle code operate against the typed route contract.
Read current contextuseRouteContext, RouteMatchReads the context associated with a specific route match.
Inspect routeruseRouter, useRouterState, RouterStateAccesses the router instance or its state when route context is not the right abstraction.
Navigate and link safelyLink, Navigate, useNavigateUses the same route tree contract that context helps type and organize.

This mapping is useful because context problems often appear as type problems. If a route cannot see auth, the fix is usually not in the component that reads it; the fix is in the root context declaration or in the object passed to createRouter. If a hook cannot infer the correct route context, check whether it is scoped to the right route or whether a route-specific API should be used. The Router API index includes getRouteApi, which is commonly useful when you want route-scoped helpers instead of relying on broad, unscoped hooks. Sources: docs/router/api/router.md

Execution Flow

A typical context flow starts before rendering. First, define the root route and declare the context type with createRootRouteWithContext. Second, build the route tree, either manually with route creation APIs or through file-based routing and generation. Third, call createRouter with the route tree and concrete context values. Fourth, let route lifecycle code use that context for decisions such as redirects, not-found handling, data loading, or dependency access. Finally, route components and shared UI read route context or router state through hooks when they need information from the active match.

That sequence keeps dependency injection close to routing. Authentication is a good example: the router can be created with an auth object, parent routes can protect an authenticated layout, and descendants can rely on the resolved context instead of duplicating auth checks. Data loading follows the same pattern. A shared query client or API client can be injected once, used by route loaders, and then coordinated with loader data and invalidation. Because Router already exposes loader, params, search, navigation, redirect, and error APIs in the same public reference, context becomes the bridge that lets those features share application services without weakening route types. Sources: docs/router/api/router.md

API Components and Hook Usage

Use useRouteContext when the component is asking, “What context applies to this route match?” This is different from useRouter, which returns the router instance, and different from useRouterState, which observes router state. The API index places all three hooks in the same Hooks section, but they solve different reader problems. Reach for route context when you need injected dependencies or parent-provided route values. Reach for router state when you need navigation status, matches, or other state-level information. Reach for the router instance when you need to call router-level methods or integrate with framework infrastructure. Sources: docs/router/api/router.md

function DashboardShell() {
  const context = useRouteContext({ from: '/dashboard' })
  return <DashboardNav user={context.auth.user} />
}

When using file-based routing, the same idea applies even though route files and route tree generation hide some of the manual wiring. The generated route map keeps route files, params, search schemas, loader outputs, and context connected to the APIs used by application code. That means context should be treated as part of the route contract, not as a side channel. If a route depends on a value, prefer declaring and injecting it through the router path so loaders, guards, and components all agree on the same type source.

Implementation Details and Design Guidance

Keep router context stable, explicit, and intentionally small. It should hold dependencies and request/application state that routes genuinely need, not every piece of UI state in the app. Values that change frequently may cause unnecessary downstream work if they are passed as new context objects on every render. For framework integrations, create the router at the boundary where you already know the required dependencies, then pass stable references such as clients, service adapters, or auth managers. Use route-local loaders and search validation for URL-driven data, and reserve context for cross-cutting dependencies.

Also separate authorization decisions from display decisions. A route guard or loader can use context to redirect unauthenticated users before rendering, while components can use useRouteContext to display the authenticated user or feature-specific UI. This division matches TanStack Router’s broader value proposition: routes are not only path matchers, but typed units that coordinate navigation, URL state, data, errors, pending UI, and context. When context is wired through the route tree, redirects, links, loader data, and component consumption remain aligned with the same application contract.

Next Steps

If you are defining context for the first time, start at the root route, write down the minimal dependency shape, and make the createRouter call satisfy that shape with concrete values. Then update parent layout routes to perform cross-cutting checks, such as authentication, and use useRouteContext only where UI needs the resolved route context. For adjacent topics, read data-loading for loaders that consume injected clients, authenticated-routes for guard patterns, router-hooks-api-reference for hook-level details, and router-options-state-events for router construction and state observation.