Documentation Index
Fetch the complete documentation index at: https://mintlify.com/TanStack/router/llms.txt
Use this file to discover all available pages before exploring further.
Components
TanStack Start provides specialized React components for building full-stack applications.
StartClient
StartClient is the root client component that hydrates your application in the browser.
Usage
// src/entry-client.tsx
import { StartClient } from '@tanstack/react-start'
import { hydrateRoot } from 'react-dom/client'
hydrateRoot(document.getElementById('root')!, <StartClient />)
How It Works
The StartClient component:
- Calls
hydrateStart() to restore the router state from the server
- Wraps your application with
RouterProvider
- Uses
Await to handle the hydration promise
- Signals when hydration is complete for cleanup
import { Await, RouterProvider } from '@tanstack/react-router'
import { hydrateStart } from './hydrateStart'
let hydrationPromise: Promise<AnyRouter> | undefined
export function StartClient() {
if (!hydrationPromise) {
hydrationPromise = hydrateStart()
}
return (
<Await
promise={hydrationPromise}
children={(router) => <RouterProvider router={router} />}
/>
)
}
StartServer
StartServer is the root server component used during server-side rendering.
Usage
// src/entry-server.tsx
import { StartServer } from '@tanstack/react-start/server'
import { createMemoryHistory } from '@tanstack/react-router'
export async function render(request: Request) {
const router = createRouter()
const memoryHistory = createMemoryHistory({
initialEntries: [new URL(request.url).pathname],
})
router.update({ history: memoryHistory })
await router.load()
return <StartServer router={router} />
}
How It Works
StartServer is a thin wrapper around RouterProvider for the server:
import { RouterProvider } from '@tanstack/react-router'
import type { AnyRouter } from '@tanstack/router-core'
export function StartServer<TRouter extends AnyRouter>(props: {
router: TRouter
}) {
return <RouterProvider router={props.router} />
}
Entry Points
Client Entry
Your client entry file renders the StartClient component:
// src/entry-client.tsx
import { StartClient } from '@tanstack/react-start'
import { hydrateRoot } from 'react-dom/client'
hydrateRoot(
document.getElementById('root')!,
<StartClient />
)
Server Entry
Your server entry file uses StartServer for SSR:
// src/entry-server.tsx
import { renderToString } from 'react-dom/server'
import { StartServer } from '@tanstack/react-start/server'
import { createMemoryHistory } from '@tanstack/react-router'
import { createRouter } from './router'
export async function render(request: Request) {
const router = createRouter()
const history = createMemoryHistory({
initialEntries: [new URL(request.url).pathname],
})
router.update({ history })
await router.load()
const html = renderToString(<StartServer router={router} />)
return new Response(html, {
headers: { 'Content-Type': 'text/html' },
})
}
Hydration
The hydration process seamlessly transfers state from server to client.
hydrateStart
The hydrateStart function restores the router instance on the client:
import { hydrateStart } from '@tanstack/react-start/client'
// Called by StartClient
const router = await hydrateStart()
Hydration Flow
- Server: Router state is serialized and embedded in the HTML
- Client:
hydrateStart() deserializes the state
- Client: Router is initialized with the restored state
- Client: React hydrates the DOM without re-rendering
- Cleanup: Hydration signal triggers any necessary cleanup
Router Integration
Start components integrate with TanStack Router components:
import {
Link,
Outlet,
RouterProvider,
createRootRoute,
createRoute,
createRouter,
} from '@tanstack/react-router'
import { StartClient } from '@tanstack/react-start'
const rootRoute = createRootRoute({
component: () => (
<div>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
<Outlet />
</div>
),
})
const indexRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/',
component: () => <h1>Home</h1>,
})
const routeTree = rootRoute.addChildren([indexRoute])
const router = createRouter({ routeTree })
// In your app
<StartClient />
Scripts Component
The Scripts component injects client bundles into your HTML:
import { Scripts } from '@tanstack/react-router'
function RootDocument({ children }: { children: React.ReactNode }) {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
{children}
<Scripts />
</body>
</html>
)
}
Head Management
Manage document head with HeadContent:
import { HeadContent, createRootRoute } from '@tanstack/react-router'
export const Route = createRootRoute({
head: () => ({
meta: [
{ charSet: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ name: 'description', content: 'My TanStack Start app' },
],
links: [
{ rel: 'stylesheet', href: '/styles/app.css' },
{ rel: 'icon', href: '/favicon.ico' },
],
}),
component: () => (
<html>
<head>
<HeadContent />
</head>
<body>
<Outlet />
</body>
</html>
),
})
Streaming
Stream HTML and data to the client:
import { renderToPipeableStream } from 'react-dom/server'
import { StartServer } from '@tanstack/react-start/server'
export async function render(request: Request) {
const router = createRouter()
// ... setup router
return new Promise((resolve) => {
const { pipe } = renderToPipeableStream(
<StartServer router={router} />,
{
onShellReady() {
const stream = new TransformStream()
pipe(stream.writable)
resolve(new Response(stream.readable, {
headers: { 'Content-Type': 'text/html' },
}))
},
}
)
})
}
Deferred Data
Use Await and Suspense for deferred data loading:
import { Suspense } from 'react'
import { Await, createFileRoute } from '@tanstack/react-router'
import { fetchData } from '~/utils/data'
export const Route = createFileRoute('/data')({
loader: async () => ({
// Deferred - resolves after initial render
deferredData: fetchData(),
}),
component: DataPage,
})
function DataPage() {
const { deferredData } = Route.useLoaderData()
return (
<Suspense fallback={<div>Loading data...</div>}>
<Await promise={deferredData}>
{(data) => <div>{data}</div>}
</Await>
</Suspense>
)
}
Error Boundaries
Handle errors with error boundaries:
import { ErrorComponent, createRootRoute } from '@tanstack/react-router'
export const Route = createRootRoute({
errorComponent: ({ error }) => (
<div>
<h1>Something went wrong</h1>
<pre>{error.message}</pre>
</div>
),
})
Not Found
Handle 404 errors:
import { createRootRoute } from '@tanstack/react-router'
export const Route = createRootRoute({
notFoundComponent: () => (
<div>
<h1>404 - Page Not Found</h1>
<Link to="/">Go Home</Link>
</div>
),
})
Best Practices
Single Root Component
Use StartClient once at your application root:
// ✅ Good
hydrateRoot(document.getElementById('root')!, <StartClient />)
// ❌ Bad - don't nest StartClient
function App() {
return <StartClient /> // Already rendered at root
}
Server vs Client Imports
Use the correct import for each environment:
// Client entry
import { StartClient } from '@tanstack/react-start'
// Server entry
import { StartServer } from '@tanstack/react-start/server'
Document Structure
Always render a complete HTML document:
function RootDocument({ children }: { children: React.ReactNode }) {
return (
<html>
<head>
<HeadContent />
</head>
<body>
{children}
<Scripts />
</body>
</html>
)
}
Hydration Mismatch
Avoid hydration mismatches by ensuring server and client render the same content:
// ❌ Bad - will cause hydration mismatch
function Component() {
const [mounted, setMounted] = useState(false)
useEffect(() => setMounted(true), [])
return <div>{mounted ? 'Client' : 'Server'}</div>
}
// ✅ Good - consistent across server and client
function Component() {
return <div>Consistent content</div>
}
API Reference
StartClient
Client-side root component.
Props: None
Example:
import { StartClient } from '@tanstack/react-start'
hydrateRoot(document.getElementById('root')!, <StartClient />)
StartServer
Server-side root component.
Props:
router: AnyRouter - Router instance
Example:
import { StartServer } from '@tanstack/react-start/server'
<StartServer router={router} />
hydrateStart
Hydrates the router on the client.
Returns: Promise<AnyRouter>
Example:
import { hydrateStart } from '@tanstack/react-start/client'
const router = await hydrateStart()