Server-Side Rendering
Purpose and Scope
Server-side rendering, or SSR, means producing the initial application document on the server before the browser takes over interactive routing. In TanStack Router, the important idea is that SSR is not a separate routing model. The same route tree, route definitions, loader data APIs, error boundaries, redirects, and outlet composition that power client navigation also define what the server must match and prepare for the first render. The Router API index exposes those primitives as first-class public concepts, including router creation, file routes, loaders, deferred values, boundaries, links, navigation helpers, and router state inspection.
Sources: docs/router/api/router.md
This page focuses on how to think about SSR in a Router application rather than on a framework-specific server adapter. Router provides the route and data contract: it knows how routes are created, how route matches are represented, how params and search are consumed, how loader data is read, and how route errors or redirects are identified. Framework packages and TanStack Start can then decide how to connect that contract to a server runtime, HTML streaming, hydration, and deployment. Treat Router SSR as the shared route-data layer underneath those integrations.
Relevant Source Files
- docs/router/api/router.md: Router API index that lists the public functions, components, hooks, and types relevant to SSR-compatible route trees, data loading, deferred rendering, navigation, errors, and router state.
System-to-Code Mapping
The API index shows that server-rendered applications start from the same construction functions used in client-rendered applications. createRouter creates the router instance, while createRoute, createFileRoute, createRootRoute, and createRootRouteWithContext describe the route tree that the server and client must agree on. File-based SSR examples should therefore be read as examples of route-tree generation, not as a different runtime abstraction. A file route still contributes route options, loaders, components, and nested children to the same Router type surface used after hydration.
Sources: docs/router/api/router.md
| SSR concern | Router API surface | How it participates |
|---|---|---|
| Route tree construction | createRouter, createRoute, createFileRoute, createRootRoute, createRootRouteWithContext | Defines the routes that can be matched on the server and reused by the client. |
| Nested document UI | <Outlet> | Lets matched parent and child routes compose the rendered HTML tree. |
| Data preparation | useLoaderData, useLoaderDeps, route loaders via route options | Gives components typed access to data that was resolved for the current match. |
| Streaming-friendly values | defer, <Await>, useAwaited | Separates critical route data from values that can resolve later in a streamed UI. |
| Browser-only sections | <ClientOnly> | Marks UI that should render only after the client runtime is available. |
| Failures and redirects | redirect, notFound, isRedirect, isNotFound, <CatchBoundary>, <CatchNotFound> | Provides route-level control flow and display boundaries for server and client navigation. |
| Hydration observation | useRouterState, useMatches, useLocation | Lets components read the matched state that must remain consistent across server and client. |
Execution Flow
A typical SSR flow begins by constructing the router with the same route tree that the browser bundle will use. The server receives a URL, asks the router layer to resolve the location against route definitions, and prepares the route matches required for the initial document. Components render through nested outlets, and route components read typed data through hooks such as useLoaderData, useParams, useSearch, and useRouteContext. After the HTML reaches the browser, the client creates an equivalent router instance so hydration can continue from the same location and matched route structure.
Sources: docs/router/api/router.md
File-based routing fits this flow because createFileRoute and createLazyFileRoute are public route creation APIs, not merely build-tool conveniences. A file-based app can generate a route tree where each route module owns its path segment, loader, component, boundary, and static options. For SSR, that generated tree becomes the server's matching contract and the client's hydration contract. Lazy routes and lazy route components can still be used, but the SSR integration needs to decide which route code is loaded before the initial render and which chunks are deferred until later navigation.
Streaming and Deferred Data Patterns
Streaming is useful when a page has a small amount of data needed for the shell and slower data that can arrive later. The Router API index exposes defer, <Await>, and useAwaited, which together describe Router's public vocabulary for deferred values. In an SSR setup, critical route data can be resolved before the first bytes are sent, while non-critical promises can be represented as deferred data and rendered behind an awaiting boundary. This keeps the route tree responsible for declaring data needs while allowing the rendering integration to stream progressively.
Sources: docs/router/api/router.md
A practical pattern is to keep layout, navigation, and above-the-fold route data synchronous enough for the first server render, then wrap slower panels with <Await> or read them through useAwaited where appropriate. This makes the loading boundary explicit in the component tree instead of hiding it in an ad hoc server handler. If a section depends on browser-only APIs, <ClientOnly> belongs near that section so the server document can still render a stable shell. These APIs help preserve the mental model that routes own data and components own presentation boundaries.
Error, Redirect, and Not-Found Handling
SSR must turn route control flow into the correct server response and into a useful client UI. The Router API index lists redirect, notFound, isRedirect, and isNotFound, which are the public helpers for producing and recognizing those outcomes. It also lists <CatchBoundary>, <CatchNotFound>, <ErrorComponent>, <NotFoundComponent>, and <DefaultGlobalNotFound>, which are the rendering side of the same story. An SSR integration can classify thrown redirect or not-found results while the route tree supplies the nearest boundary for user-facing output.
Sources: docs/router/api/router.md
This matters because server rendering happens before normal browser navigation exists. A redirect should usually become a server redirect response when detected during the initial match or data phase, while a not-found route can become a not-found status and still render a route-aware page. Once hydrated, the same helpers continue to work during client navigation. Keeping these paths unified reduces the chance that a route behaves one way on refresh and another way when reached through <Link> or useNavigate.
Compact API Reference
- Router construction:
createRouter,createRoute,createFileRoute,createLazyFileRoute,createRootRoute,createRootRouteWithContext. - Route composition:
<Outlet>,Route Type,RouteOptions Type,RouteMatch Type,RouterState Type. - Data and streaming:
defer,<Await>,useAwaited,useLoaderData,useLoaderDeps. - Location and matching reads:
useLocation,useMatches,useMatch,useParams,useSearch,useRouteContext,useRouterState. - Navigation after hydration:
<Link>,<Navigate>,useNavigate,linkOptions,NavigateOptions Type,LinkOptions Type. - Boundaries and control flow:
redirect,notFound,isRedirect,isNotFound,<CatchBoundary>,<CatchNotFound>,<ErrorComponent>,<NotFoundComponent>.
Next Steps
When building an SSR application, first make the route tree deterministic between server and client, then decide which data must block the initial render and which data can be deferred. Next, add route-level boundaries for redirects, not-found states, and unexpected errors so refreshes and client transitions produce the same user experience. For file-based applications, review the file-based routing and route generation pages next. For full-document SSR, streaming, server functions, and deployment concerns, continue to the TanStack Start overview and rendering pages, which build on the Router primitives described here.