Skip to content
Dashboard

TanStack Start: A route-first full-stack React framework

Link to headingBuild full-stack React with TanStack Start

Link to headingRoutes define your app's shape

Link to headingSetup stays close to the build tool

src/
router.tsx
routeTree.gen.ts
routes/
__root.tsx
index.tsx
posts/
$postId.tsx

Link to headingFile routes carry types

src/routes/products/$productId.tsx
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/products/$productId')({
component: ProductPage,
})
function ProductPage() {
const { productId } = Route.useParams()
return <ProductDetail productId={productId} />
}

src/routes/orders.tsx
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'
const orderSearch = z.object({
status: z.enum(['open', 'closed', 'all']).default('open'),
page: z.number().int().min(1).default(1),
sort: z.enum(['newest', 'oldest']).default('newest'),
})
export const Route = createFileRoute('/orders')({
validateSearch: orderSearch,
component: OrdersPage,
})
function OrdersPage() {
const search = Route.useSearch()
return <OrdersTable filters={search} />
}

Link to headingLoaders make data explicit

src/routes/orders.tsx
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'
const orderSearch = z.object({
status: z.enum(['open', 'closed', 'all']).default('open'),
page: z.number().int().min(1).default(1),
})
export const Route = createFileRoute('/orders')({
validateSearch: orderSearch,
loaderDeps: ({ search }) => ({
status: search.status,
page: search.page,
}),
loader: ({ deps, abortController }) =>
fetchOrders(deps, { signal: abortController.signal }),
component: OrdersPage,
})
function OrdersPage() {
const orders = Route.useLoaderData()
return <OrdersTable orders={orders} />
}

Link to headingServer functions are explicit

src/server/orders.ts
import { createServerFn } from '@tanstack/react-start'
import { z } from 'zod'
const input = z.object({
status: z.enum(['open', 'closed', 'all']),
page: z.number().int().min(1),
})
export const getOrders = createServerFn({ method: 'GET' })
.validator(input)
.handler(async ({ data }) => {
return fetchOrdersFromDatabase(data)
})

Link to headingSSR streams the document

Link to headingDeferred hydration controls startup work

import { Hydrate } from '@tanstack/react-start'
import { visible } from '@tanstack/react-start/hydration'
export function ProductPage() {
return (
<Hydrate when={visible({ rootMargin: '400px' })}>
<Reviews />
</Hydrate>
)
}

Link to headingVercel deploys Start directly

Link to headingA framework comparison

Link to headingWhen TanStack Start is the right choice for your application

Link to headingFAQ

Link to headingWhat is TanStack Start?

Link to headingHow is TanStack Start different from Next.js App Router?

Link to headingCan TanStack Start deploy to Vercel?

Ready to deploy?