00 An explorable architecture comparison
Two answers to
the same question.
Next.js 16 and TanStack Start are both full-stack React frameworks, asked the same thing: where should the work of your app happen? Their answers run opposite directions. Next starts on the server and pushes outward; TanStack starts on the client and reaches back. Almost every difference — caching, deployment, type-safety, mental model — falls out of that one choice. Everything below is clickable.
Built around React Server Components. Render on the server, ship the minimum JavaScript, opt pieces back into dynamic with explicit caching. Opinionated, batteries-included, tuned for content and scale.
A type-safe SPA that grew a server. Keep React's classic client model, then add server functions and SSR where you need them. Composable, portable, obsessed with end-to-end types.
Reflects Next.js 16.2 and TanStack Start v1.0 Release Candidate, as of June 2026. Both move fast — pin versions and check release notes before committing.
01 The one split everything follows from
Where does the work live?
A web app does a handful of jobs: match a route, fetch data, render the first screen, hydrate, then handle clicks and mutations. The frameworks disagree about the default home for each job. Read across each row to see where the two place the same work.
server runs before/around the response·client runs in the browser·both isomorphic
02 The machinery under each name
What they're built on.
Neither framework is one thing. Next.js is a tightly integrated Vercel toolchain; TanStack Start is a composition of independent libraries that also stand on their own. Tap any layer to expand it.
The shape is the point: Next.js owns its bundler, compiler and caching model end to end. TanStack Start borrows best-in-class tools — Vite, Nitro, Router — then wires them together.
03 One request, frame by frame
The live trace.
Press play and watch a single request travel three swim-lanes — Server, the Network, and the Browser. Scrub it, step it, or click any node to crack it open. Toggle the framework to see how Next front-loads the server while TanStack hands off to the browser early.
Press play to trace the request
Each frame highlights as the request moves down its lane.
04 The same feature in both
Read a user, then change their name.
The clearest way to feel a framework is to write the same thing twice — load data on the server, mutate it from the client — in each idiom. Same job, two philosophies of how server and client talk.
// app/users/[id]/page.tsx — a React Server Component import { updateName } from './actions' export default async function Page({ params }) { const { id } = await params // params is async in v16 const user = await getUser(id) // runs on the server, no fetch() return ( <form action={updateName}> // progressive-enhancement form <input name="name" defaultValue={user.name} /> <input type="hidden" name="id" value={user.id} /> <button>Save</button> </form> ) } // app/users/[id]/actions.ts 'use server' // this whole module is server-only export async function updateName(form: FormData) { await db.user.update({ where: { id: String(form.get('id')) }, data: { name: String(form.get('name')) }, }) }
// src/routes/users.$id.tsx import { createFileRoute } from '@tanstack/react-router' import { createServerFn } from '@tanstack/react-start' // server fns are RPC in the browser, direct calls on the server const getUser = createServerFn({ method: 'GET' }) .validator((id: string) => id) // runtime + type validation .handler(({ data: id }) => db.user.find(id)) const updateName = createServerFn({ method: 'POST' }) .validator((d: { id: string; name: string }) => d) .handler(({ data }) => db.user.update(data.id, { name: data.name })) export const Route = createFileRoute('/users/$id')({ loader: ({ params }) => getUser({ data: params.id }), // SSR + client component: User, }) function User() { const user = Route.useLoaderData() // fully typed, no generics return <button onClick={() => updateName({ data: { id: user.id, name: 'New' } })}> {user.name} </button> }
05 Where each can actually run
Deploy anywhere?
Runtime portability is one of the sharpest differences. Next.js is happiest on Vercel's Node + Edge; TanStack Start builds through Nitro for almost any target. Pick a deploy target and see how each framework reaches it.
Pick a target above.
Pick a target above.
06 Why each was built this way
What each was designed to solve.
Architectures are answers to specific pain. Next.js 16 is a course-correction on its own past; TanStack Start is a reaction to where the React ecosystem went.
Next.js 16 — tame the server-first model it created
- End implicit caching. v16 flips to dynamic by default, with an explicit
use cachedirective when you opt in. - Finish Partial Prerendering. Cache Components ship a static shell instantly and stream dynamic holes through Suspense.
- Make builds fast. Turbopack (Rust) is the stable default: 2–5× faster builds, ~10× Fast Refresh, on-disk caching.
- Ship less client JS. Lean fully into React Server Components; the React Compiler removes manual memoization.
TanStack Start — keep React's client soul, add a server
- Escape RSC complexity. A path without the
'use client'boundary dance and its hydration mismatches. - Total type safety. Types flow unbroken from the URL through loaders to server functions — all checked at compile time.
- Deploy anywhere. Vite + Nitro means the same app runs on Node, Cloudflare, Netlify, Vercel or static output.
- Reward the TanStack stack. Compose naturally with Query and Router as one coherent client-first system.
07 Match the tool to your goal
How to choose.
There's no winner in the abstract — only a fit for your project. Tick the statements that are true for what you're building. The needle leans toward the framework whose design those goals reward.
08 Side by side, no spin
The honest sheet.
A flat comparison of the dimensions that actually decide projects — including where TanStack is still catching up.
| Dimension | Next.js 16 | TanStack Start |
|---|---|---|
| Mental model | Server-first via React Server Components; 'use client' for interactivity | Client-first SPA; createServerFn reaches the server as typed RPC |
| Maturity | Stable, mature; v16.2, years in production | v1.0 Release Candidate — feature-complete, API stable, still young |
| Bundler / dev | Turbopack (Rust), stable default; webpack via flag | Vite (also Rsbuild); instant HMR, huge plugin ecosystem |
| Routing | App Router, file-based; proxy.ts for edge middleware | TanStack Router; file-based, fully type-safe, URL-as-state |
| Type safety | Good; route params/loaders typed, RSC props inferred | Best-in-class; types flow URL → loader → server fn end to end |
| Data & caching | Cache Components: use cache + PPR; dynamic by default | Router loaders + SWR; pairs with TanStack Query for client cache |
| RSC support | Core paradigm — fully supported today | Not yet; in active development as a non-breaking v1.x addition |
| Built-in optimizations | Image, Font, script optimization, i18n — batteries included | Minimal; bring your own (no direct Image/Font equivalents) |
| Runtime / deploy | Node + Edge; smoothest on Vercel, self-hostable | Nitro universal: Node, Cloudflare, Netlify, Vercel, static (Node ≥22.12) |
| Ecosystem | Huge community, many examples, Vercel enterprise backing | Smaller but fast-growing (~15% adoption, ~50% interested, early 2026) |
| Best at | Content/SEO sites, commerce, scale, RSC-native apps | App-like products, dashboards, type-safe data UIs, portable deploys |