Rendering and Hydration

Purpose and Scope

TanStack Start is the full-stack layer built around the Router application contract: a typed route tree, route loaders, URL state, and navigation semantics. Rendering and hydration are where that contract becomes a document response on the server and an interactive app in the browser. Start owns the server work, including full-document rendering and streaming, while Router continues to define the route tree, route components, data boundaries, and navigation APIs that both environments share. Read this page as the bridge between Start’s server-first rendering model and the Router primitives visible in this repository’s API documentation.

Sources: docs/router/api/router.md, docs/router/api/file-based-routing.md

The practical reader problem is deciding which primitive belongs in which rendering phase. Route files define the shape of the app, generated route trees connect those files to the router, asynchronous route components allow route-level code splitting, deferred data can suspend during rendering, and catch boundaries protect the screen from render-time failures. Start adds server rendering, streaming, server functions, and deployment output on top of those pieces, but the same route options and route components remain the source of truth. That separation is important: rendering can move between server and client without changing how links, loaders, params, and route matches are authored.

Sources: docs/router/api/router.md, docs/router/api/router/AsyncRouteComponentType.md, docs/router/api/router/awaitComponent.md, docs/router/api/router/catchBoundaryComponent.md

Relevant Source Files

  • docs/router/api/file-based-routing.md - Documents file-based routing configuration, including the routes directory, generated route tree output, virtual route configuration, route file filtering, route tokens, and automatic code splitting controls that influence what Start can render.
  • docs/router/api/router.md - Provides the Router API index for route creation, lazy route functions, deferred data, Router creation, components such as Await, CatchBoundary, ClientOnly, Outlet, Link, and Navigate, hooks, and public types used by rendering code.
  • docs/router/api/router/ActiveLinkOptionsType.md - Defines active and inactive link prop behavior, which matters after hydration because server-rendered anchors must continue to reflect route activity as client navigation takes over.
  • docs/router/api/router/AsyncRouteComponentType.md - Defines the async route component shape and optional preload method used by code-split route components before or during navigation and rendering.
  • docs/router/api/router/awaitComponent.md - Documents the Await component, its promise and child render-function props, and its suspend, reject, and resolved rendering behavior for deferred data.
  • docs/router/api/router/catchBoundaryComponent.md - Documents CatchBoundary, including reset keys, children, error components, and catch callbacks for rendering fallback UI after thrown errors.

Core Rendering Primitives

Start rendering begins with the route tree rather than with an isolated page component. File-based routing configuration identifies where route files live and where the generated route tree is written, and the generated tree is the artifact that lets the router know the available matches before rendering. The file-based routing reference also calls out options for virtual routes, route file prefixes, ignored files, route tokens, and automatic code splitting. In a Start app, those settings affect the server and client because both sides need to agree on the same route IDs, layouts, and component boundaries before hydration can safely reuse the server output.

Sources: docs/router/api/file-based-routing.md

The Router API index is the public map for the components and functions that participate in rendering. Route creation functions define the tree, Router creation wires the tree into an instance, deferred data helpers and Await coordinate pending asynchronous values, and boundary components define how failures are displayed. The same index also lists ClientOnly, Outlet, Link, Navigate, route hooks, and types for state, options, events, and matches. For Start developers, this means hydration is not a separate routing system. Hydration is the browser resuming a Router-driven document that was already matched and rendered on the server.

Sources: docs/router/api/router.md

System-to-Code Mapping

Rendering concernRepository-backed primitiveWhat it controls
Route discoveryFile-based routing configurationThe route directory, generated route tree location, virtual routes, ignored files, and route filename tokens.
Route-level code splittingAsyncRouteComponentA code-split route component that can expose a preload method.
Deferred renderingAwaitSuspends for pending promises, throws rejected errors, and renders children with resolved values.
Error recoveryCatchBoundaryCatches child errors, renders an error component, and resets when a reset key changes.
Hydrated navigation stylingActiveLinkOptionsApplies active or inactive anchor props once the current route is known.

A useful mental model is to treat the generated route tree as the shared manifest, asynchronous route components as the component loading boundary, Await as the data readiness boundary, and CatchBoundary as the recovery boundary. Server rendering can stream the shell and route data as they become available, but each boundary still has a local purpose. Async components make the JavaScript graph loadable in chunks. Await lets a component describe how to render once a promise resolves. CatchBoundary prevents one failed subtree from forcing the entire app to become unusable. Active link options then preserve navigation feedback after the browser has hydrated the document.

Sources: docs/router/api/router/AsyncRouteComponentType.md, docs/router/api/router/awaitComponent.md, docs/router/api/router/catchBoundaryComponent.md, docs/router/api/router/ActiveLinkOptionsType.md

Execution Flow

A typical Start render begins when a request URL is matched against the Router route tree. File-based route settings determine which files are part of that tree and where the generated route tree module is emitted. Once a route match is known, route loaders and component loading can begin before React commits the final UI. Start can perform this work on the server so the response includes document HTML, route data, head information, and useful pending UI. On the client, hydration attaches event handlers and Router state to that same structure rather than rediscovering an unrelated page model.

Sources: docs/router/api/file-based-routing.md, docs/router/api/router.md

Selective SSR and deferred hydration are best understood as boundary placement decisions. A component that must only run in the browser should be isolated from the server-rendered portion of the tree, while route data that can be delayed should be represented as a promise consumed by Await or, in React versions that support it, the native use hook. The Await API explicitly describes pending promises as suspending, rejected promises as throwing, and resolved promises as invoking a child render function. Those behaviors give Start a predictable way to stream useful HTML while preserving a typed path for later data completion.

Sources: docs/router/api/router/awaitComponent.md, docs/router/api/router.md

Hydration errors often come from server and client output disagreeing. In a Router-first Start app, the most important safeguards are stable route configuration, consistent generated route tree output, and deterministic rendering around data and browser-only state. If a route file is accidentally ignored by file routing options, or if route token settings do not match the expected layout structure, the server-rendered document may not correspond to the client route tree. Likewise, unresolved promises and thrown render errors need explicit boundaries so the app can show pending or fallback UI instead of collapsing during hydration.

Sources: docs/router/api/file-based-routing.md, docs/router/api/router/catchBoundaryComponent.md, docs/router/api/router/awaitComponent.md

API and Configuration Reference

The file-based routing reference names several configuration fields that directly influence rendering topology. The required routes directory points at the application’s route files, and the required generated route tree path identifies where the route tree artifact is saved. Virtual route configuration can add route definitions outside the normal file walk. Prefix and ignore settings decide which files become routes, while route tokens identify layout route files. The auto code splitting option is particularly relevant to Start rendering because route-level boundaries can reduce the JavaScript needed for the first hydrated screen while still allowing navigation to preload future route components.

Sources: docs/router/api/file-based-routing.md

The async route component type defines a route component that is also able to expose a preload method returning a promise. That is a small type, but it has large rendering implications. A route component can be discovered as part of a match, preloaded before navigation, and then rendered when the route becomes active. In Start, this cooperates with server rendering and browser hydration because code loading is represented as an explicit route concern rather than an ad hoc dynamic import scattered throughout the component tree. Keep route-level laziness close to route definitions so preloading remains predictable.

Sources: docs/router/api/router/AsyncRouteComponentType.md

The Await component is the reference point for deferred data rendering in React 18. It accepts a required promise and a required child function that receives the resolved value. The documented return behavior is intentionally aligned with suspense semantics: pending values suspend, rejected values throw, and resolved values render through the child function. That makes Await a good boundary for streamed content and for route loader data that should not block the entire document. In React 19, the same source notes that the use hook can replace Await, so new code should choose the primitive that matches the React version.

Sources: docs/router/api/router/awaitComponent.md

The CatchBoundary component is the rendering error primitive described by the Router API. It receives children to render during the normal path, an optional error component for the failure path, an optional catch callback, and a required reset-key function. The reset key matters during navigation and hydration because it lets the boundary declaratively clear prior failure state when the key changes. When building Start screens, place boundaries around route regions where loader data, deferred values, or browser-only widgets can fail independently. That gives users a recoverable screen and gives developers a precise callback for logging or diagnostics.

Sources: docs/router/api/router/catchBoundaryComponent.md

Hydration Patterns and Edge Cases

Use ActiveLinkOptions as a small but important example of post-hydration continuity. The type extends link options with active and inactive anchor props, each of which can be either an attributes object or a function returning attributes. On the server, links render as anchors in the document. After hydration, Router knows the current match and can keep visual state aligned with navigation. Avoid duplicating active-route logic in unrelated components when link options already describe the intended active and inactive styling behavior in a typed way.

Sources: docs/router/api/router/ActiveLinkOptionsType.md

When troubleshooting hydration, work from the route tree outward. First confirm that file-based routing options include the route file you expect and generate the same route tree module used by the client bundle. Next, check asynchronous route components and whether their preload behavior is being exercised before navigation or render. Then inspect deferred data boundaries: a promise that rejects should flow to a catch boundary, and a pending promise should suspend at the intended level. Finally, review browser-only components and link styling after hydration to ensure the client is enhancing the server document rather than replacing it unexpectedly.

Sources: docs/router/api/file-based-routing.md, docs/router/api/router/AsyncRouteComponentType.md, docs/router/api/router/awaitComponent.md, docs/router/api/router/catchBoundaryComponent.md, docs/router/api/router/ActiveLinkOptionsType.md

Next Steps

For implementation work, start with the route files and generated route tree configuration, then add rendering boundaries deliberately. Use route-level async components when a route can be split from the initial bundle. Use Await or the React 19 equivalent for deferred values that should stream or suspend locally. Wrap unstable regions in CatchBoundary with a reset key that changes when the route or data identity changes. After the page hydrates, rely on Router link options and hooks rather than custom URL parsing so navigation state remains tied to the typed route tree. Related pages should cover Start routing entry points, server functions, data loading, code splitting, and error boundaries in more depth.