Choosing a full-stack React framework? This comparison focuses on the full-stack framework features that distinguish TanStack Start from Next.js and React Router (v7 Framework Mode).
🚨 IMPORTANT: Looking for routing features?
TanStack Start is built on TanStack Router, which provides industry-leading type-safe routing capabilities. For a comprehensive comparison of routing features (nested routes, search params, type safety, loaders, etc.), please see:
📖 TanStack Router Comparison vs React Router / Next.js →
That comparison covers all the routing features in detail. This page focuses specifically on full-stack framework capabilities like SSR, server functions, middleware, deployment, and more.
While we aim to provide an accurate and fair comparison, please note that this table may not capture every nuance or recent update of each framework. We recommend reviewing the official documentation and trying out each solution to make the most informed decision for your specific use case.
If you find any discrepancies or have suggestions for improvement, please don't hesitate to contribute via the "Edit this page on GitHub" link at the bottom of this page or open an issue in the TanStack Router GitHub repository.
Feature/Capability Key:
| TanStack Start | Next.js (Website) | React Router (Website) | |
|---|---|---|---|
| Github Repo / Stars | |||
| Bundle Size | ❓ | ❓ | |
| -- | -- | -- | -- |
| Routing Features (See Full Comparison) | ✅ Built on TanStack Router | ✅ File-based App Router | ✅ File-based Nested Routes |
| -- | -- | -- | -- |
| Full-Stack Features | -- | -- | -- |
| SSR | ✅ | ✅ | ✅ |
| Streaming SSR | ✅ | ✅ | ✅ |
| Selective SSR (per-route) | ✅ | 🔶 | 🔶 |
| SPA Mode | ✅ | 🔶 (via "use client") | ✅ |
| Built-in Client-Side SWR Caching | ✅ (via TanStack Router) | 🔶 (fetch cache only) | 🛑 |
| Data Fetching Library Integration | ✅ (Official TanStack Query, Apollo, etc.) | 🔶 (manual integration) | 🔶 (manual integration) |
| Static Prerendering (SSG) | ✅ | ✅ | ✅ |
| Incremental Static Regeneration (ISR) | ✅ (via Cache-Control headers) | ✅ (Proprietary) | ✅ (via Cache-Control headers) |
| React Server Components | 🛑 (In active development) | ✅ | 🟡 (Experimental) |
| Server Functions | ✅ (RPC-based) | ✅ (Server Actions) | ✅ (Actions) |
| Server Function Client Middleware | ✅ | 🛑 | 🛑 |
| Server Function Server Middleware | ✅ | 🛑 | ✅ |
| Request Middleware (All Routes) | ✅ | ✅ | ✅ |
| Server Function Input Validation | ✅ | 🔶 (manual) | 🔶 (manual) |
| API Routes / Server Routes / Resource Routes | ✅ | ✅ | ✅ |
| <Form> API | 🛑 | 🟠 (via React 19 useActionState) | ✅ |
| -- | -- | -- | -- |
| Developer Experience | -- | -- | -- |
| Devtools | ✅ | 🛑 | 🟠 (3rd party) |
| CLI Tooling | ✅ | ✅ | ✅ |
| Dev Server Startup Speed | ✅ (Fast) | 🛑 (Slow) | ✅ (Fast) |
| HMR Speed | ✅ (Fast, Vite) | 🛑 (Slow, Webpack/Turbopack) | ✅ (Fast, Vite) |
| Dev Navigation Speed | ✅ | 🟡 | ✅ |
| Dev Resource Usage (CPU/RAM) | ✅ (Lightweight) | 🛑 (Heavy) | ✅ (Lightweight) |
| TypeScript Support | ✅ | ✅ | ✅ |
| Type-First Architecture | ✅ | 🛑 | 🛑 |
| -- | -- | -- | -- |
| Deployment & Hosting | -- | -- | -- |
| Deployment Flexibility | ✅ (Any Vite-compatible host) | 🟡 (Optimized for Vercel, possible elsewhere) | ✅ (Multiple adapters) |
| Edge Runtime Support | ✅ | ✅ | ✅ |
| Serverless Support | ✅ | ✅ | ✅ |
| Node.js Support | ✅ | ✅ | ✅ |
| Docker Support | ✅ | ✅ | ✅ |
| Static Export | ✅ | ✅ | ✅ |
| Official Cloudflare Support | ✅ | 🟡 | ✅ |
| Official Netlify Support | ✅ | 🟡 | ✅ |
| Official Vercel Support | ✅ (via Nitro) | ✅ | ✅ |
⚠️ Remember: For detailed comparisons of routing features (type safety, search params, loaders, navigation, etc.), see the TanStack Router Comparison. This page focuses on full-stack framework capabilities.
Philosophy: Maximum developer freedom with best-in-class type safety.
Philosophy: Production-ready with optimal defaults, best with Vercel.
Philosophy: Web fundamentals with progressive enhancement.
TanStack Start includes powerful built-in SWR (stale-while-revalidate) caching through TanStack Router:
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params }) => fetchPost(params.postId),
staleTime: 10_000, // Consider fresh for 10 seconds
gcTime: 5 * 60_000, // Keep in memory for 5 minutes
})
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params }) => fetchPost(params.postId),
staleTime: 10_000, // Consider fresh for 10 seconds
gcTime: 5 * 60_000, // Keep in memory for 5 minutes
})
For advanced scenarios, TanStack Start integrates seamlessly with TanStack Query:
import { queryOptions } from '@tanstack/react-query'
const postQueryOptions = (postId: string) =>
queryOptions({
queryKey: ['post', postId],
queryFn: () => fetchPost(postId),
})
export const Route = createFileRoute('/posts/$postId')({
loader: ({ context, params }) =>
context.queryClient.ensureQueryData(postQueryOptions(params.postId)),
})
function Post() {
const { postId } = Route.useParams()
const { data } = useQuery(postQueryOptions(postId))
// Automatically uses cached data from loader
}
import { queryOptions } from '@tanstack/react-query'
const postQueryOptions = (postId: string) =>
queryOptions({
queryKey: ['post', postId],
queryFn: () => fetchPost(postId),
})
export const Route = createFileRoute('/posts/$postId')({
loader: ({ context, params }) =>
context.queryClient.ensureQueryData(postQueryOptions(params.postId)),
})
function Post() {
const { postId } = Route.useParams()
const { data } = useQuery(postQueryOptions(postId))
// Automatically uses cached data from loader
}
Next.js has basic fetch caching but lacks fine-grained control and requires manual integration with libraries like React Query.
React Router doesn't have built-in client-side caching - you need to manually integrate a caching solution.
TanStack Start Server Functions:
export const getTodos = createServerFn({ method: 'GET' })
.inputValidator(zodValidator(z.object({ userId: z.string() })))
.middleware([authMiddleware])
.handler(async ({ data, context }) => {
// Fully typed data and context
return db.todos.findMany({ where: { userId: data.userId } })
})
// Call from anywhere with full type safety
const todos = await getTodos({ data: { userId: '123' } })
export const getTodos = createServerFn({ method: 'GET' })
.inputValidator(zodValidator(z.object({ userId: z.string() })))
.middleware([authMiddleware])
.handler(async ({ data, context }) => {
// Fully typed data and context
return db.todos.findMany({ where: { userId: data.userId } })
})
// Call from anywhere with full type safety
const todos = await getTodos({ data: { userId: '123' } })
Next.js Server Actions:
'use server'
export async function getTodos(userId: string) {
// Runs on server, called from client
return db.todos.findMany({ where: { userId } })
}
// Call from client component
const todos = await getTodos('123')
'use server'
export async function getTodos(userId: string) {
// Runs on server, called from client
return db.todos.findMany({ where: { userId } })
}
// Call from client component
const todos = await getTodos('123')
Key differences:
TanStack Start has two types of middleware:
This composability allows for:
const authMiddleware = createMiddleware({ type: 'function' })
.client(async ({ next }) => {
// Run auth checks on client
return next({
headers: { Authorization: `Bearer ${getToken()}` },
})
})
.server(async ({ next }) => {
// Validate auth on server
return next({ context: { user: await getUser() } })
})
const authMiddleware = createMiddleware({ type: 'function' })
.client(async ({ next }) => {
// Run auth checks on client
return next({
headers: { Authorization: `Bearer ${getToken()}` },
})
})
.server(async ({ next }) => {
// Validate auth on server
return next({ context: { user: await getUser() } })
})
Next.js has a single middleware.ts file that runs on the Edge Runtime for all requests. It cannot access server-only resources like databases and has limitations compared to Node.js runtime.
TanStack Start leverages Vite's ecosystem:
Next.js is optimized for Vercel:
Current Status:
RSC is still evolving, and TanStack Start is taking time to ensure the best possible implementation that aligns with its type-safety-first philosophy.
All three frameworks are capable of achieving excellent production performance and top Lighthouse scores. The differences come down to optimization strategies:
This is where the frameworks differ significantly:
TanStack Start & React Router:
Next.js:
Why This Matters:
Development performance directly impacts developer productivity. Faster feedback loops mean:
While Next.js's production performance is excellent, the development experience can be notably slower, especially on larger codebases or less powerful machines.
