Capítulo 42 de 57

Chapter 42: SSR

Core Idea

TanStack Router supports both non-streaming SSR (render everything, send one complete HTML response) and streaming SSR (send critical first paint immediately, stream the rest as it resolves), using a shared createRouter factory, RouterClient/RouterServer components, and automatic loader dehydration/hydration.

Key Concepts

  • Non-streaming SSR: entire page renders on the server into one HTML response including serialized hydration data; the client hydrates it into an interactive app. Implemented with defaultRenderHandler (automatic) or renderRouterToString + RouterServer (manual Wrap control).
  • Streaming SSR: critical first paint is sent immediately, remaining content streams incrementally in the same request. Implemented with defaultStreamHandler or renderRouterToStream + RouterServer.
  • createRouter shared factory: must be defined in a shared file (e.g. src/router.tsx) and called identically by both server and client entry files, ensuring consistent router configuration.
  • Automatic server history: on the server, TanStack Router automatically uses createMemoryHistory instead of createBrowserHistory (which needs window), handled for you by RouterServer.
  • Automatic loader dehydration/hydration: resolved loader data is automatically dehydrated on the server and rehydrated on the client, as long as the standard SSR setup steps are followed; deferred data additionally requires the streaming setup.
  • createRequestHandler: takes a standard web API Request and a createRouter function, returns a handler that produces a Response; used identically for both streaming and non-streaming.
  • RouterClient: client entry component (e.g. with hydrateRoot) that renders the app and implements the router's Wrap option automatically.
  • Data serialization: built-in support beyond JSON.stringify/parse for undefined, Date, Error, and FormData; more complex types (Map, Set, BigInt) require a custom serializer.
  • Framework distinction warning: TanStack Router is not Next.js/Remix/React Router; do not use src/pages/, getServerSideProps, Remix-style loader/action exports, or react-router-dom/next/ imports. Use src/routes/ + createFileRoute, and for TanStack Start, createServerFn.

Code Examples

// src/router.tsx
import { createRouter as createTanstackRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'

export function createRouter() {
  return createTanstackRouter({ routeTree })
}
declare module '@tanstack/react-router' {
  interface Register {
    router: ReturnType<typeof createRouter>
  }
}
  • What it demonstrates: the shared router factory pattern required for consistent server/client router creation, plus type registration.
// src/entry-server.tsx (streaming)
import { createRequestHandler, defaultStreamHandler } from '@tanstack/react-router/ssr/server'
import { createRouter } from './router'

export async function render({ request }: { request: Request }) {
  const handler = createRequestHandler({ request, createRouter })
  return await handler(defaultStreamHandler)
}
  • What it demonstrates: minimal streaming SSR server entry using defaultStreamHandler.
// src/entry-client.tsx
import { hydrateRoot } from 'react-dom/client'
import { RouterClient } from '@tanstack/react-router/ssr/client'
import { createRouter } from './router'

const router = createRouter()
hydrateRoot(document, <RouterClient router={router} />)
  • What it demonstrates: client-side hydration entry pairing with the server entry.

Key Takeaways

  1. Use defaultRenderHandler/defaultStreamHandler for the simplest setup; drop to renderRouterToString/renderRouterToStream + RouterServer only when you need custom Wrap providers.
  2. Streaming is required for deferred data (Await, see Ch 37) to actually stream from server to client; without it, deferred data resolves client-side only.
  3. These SSR APIs are shared internally with TanStack Start and are marked experimental, subject to change.
  4. Don't mix Next.js/Remix idioms into a TanStack Router project; the API surface and file conventions are different.

Connects To

  • Ch 37: Deferred Data Loading's SSR Streaming Lifecycle section depends entirely on the streaming SSR setup described here.
  • Ch 38: External Data Loading's dehydrate/hydrate/Wrap router options integrate directly with this chapter's SSR pipeline.
  • Ch 41: Document Head Management, <HeadContent /> and <Scripts /> render as part of the same server-rendered markup.