TanStack Start reached a stable 1.0 on October 13, 2026, after roughly a year as a release candidate. Tanner Linsley’s announcement makes a simple promise: the APIs are now a formal commitment, so you can “choose Start, learn it, ship something with it, and continue to build without wondering whether we’re going to change the entire way your application works.”
That stability is what many teams were waiting for before taking Start seriously as an alternative to Next.js. This post explains what TanStack Router and TanStack Start actually are, what is in 1.0, what changed for anyone already on the release candidate, and how to decide between Start and Next.js for a new React project.
TanStack Router vs TanStack Start: two layers, one routing model
The naming confuses people, so it is worth being precise.
- TanStack Router is a client-side router for React (and Solid). Its selling point is type safety across the whole route tree: links, path params, search params, loaders and route context are all inferred by TypeScript. You can use it on its own to build a single-page app.
- TanStack Start is a full-stack framework built on top of TanStack Router. The docs say Start “relies 100% on TanStack Router for its routing system” and adds full-document server-side rendering, streaming, server routes and API routes, type-safe server functions, middleware, and full-stack builds and deployment.
The docs are direct about when you don’t need Start: if you are certain you won’t need server-side features, use TanStack Router alone. An internal dashboard behind a login, with an existing API, is a good example. Moving from Router to Start later is a smaller step than switching frameworks because the routing code stays the same.
What is in TanStack Start 1.0
According to the 1.0 announcement and the current docs, the stable release includes:
- Type-safe, file-based routing from TanStack Router, including validated search params that act as typed URL state.
- Server functions created with
createServerFn. They run only on the server but can be called from loaders, components and event handlers, with input and output types inferred end to end. - Selective SSR: each route can render fully on the server, load only its data on the server, or run entirely on the client.
- Full-stack middleware that wraps server function calls, runs on both client and server, and carries typed context through.
- Environment functions such as
createIsomorphicFn, which give one function different implementations on the server and in the browser. - Vite and Rsbuild support for development and builds, with deployment handled through Nitro rather than adapters maintained by the TanStack team.
Two things to keep in perspective. First, React Server Components are experimental and opt-in in Start. The Server Components guide says the API “may see refinements” and will stay experimental into early v1, and it requires React 19+ with Vite 8+ or Rsbuild 2+. In the announcement, Linsley also writes that he is disappointed with parts of the RSC experience and that the team stopped using RSC on tanstack.com. If your architecture depends on Server Components, that matters. Second, Start does not need RSC for SSR or server functions, which is a deliberate design difference from the Next.js App Router.
Creating a project
The quick start offers a CLI that scaffolds a project and asks about your package manager and optional add-ons:
npx @tanstack/cli@latest create
If you prefer to build from scratch, the core packages are @tanstack/react-start and @tanstack/react-router, plus React and Vite. A minimal vite.config.ts looks like this. Note the plugin order: the docs say React’s Vite plugin must come after Start’s.
import { defineConfig } from 'vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import viteReact from '@vitejs/plugin-react'
export default defineConfig({
server: { port: 3000 },
plugins: [tanstackStart(), viteReact()],
})
You also need a src/router.tsx that exports a getRouter() function and a root route in src/routes/__root.tsx that renders the HTML document with HeadContent, Outlet and Scripts. The build-from-scratch guide has both files; the CLI generates them for you. Route files you add under src/routes are picked up by the plugin, which generates a typed route tree.
A small example: typed search params, a loader and two server functions
The route below lists tasks filtered by a ?status= search param and lets the user add one. It was built and type-checked against @tanstack/react-start 1.169.4, React 19 and Vite 8. The in-memory array stands in for a database.
// src/routes/tasks.tsx
import { createFileRoute, useRouter } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'
import { z } from 'zod'
// Stand-in for a real database
const tasks: { id: number; title: string; done: boolean }[] = []
const getTasks = createServerFn({ method: 'GET' })
.validator(z.object({ status: z.enum(['all', 'open', 'done']) }))
.handler(async ({ data }) => {
if (data.status === 'all') return tasks
return tasks.filter((t) => t.done === (data.status === 'done'))
})
const addTask = createServerFn({ method: 'POST' })
.validator(z.object({ title: z.string().min(1).max(200) }))
.handler(async ({ data }) => {
const task = { id: tasks.length + 1, title: data.title, done: false }
tasks.push(task)
return task
})
export const Route = createFileRoute('/tasks')({
validateSearch: z.object({
status: z.enum(['all', 'open', 'done']).catch('all'),
}),
loaderDeps: ({ search }) => ({ status: search.status }),
loader: ({ deps }) => getTasks({ data: deps }),
component: TasksPage,
})
function TasksPage() {
const tasks = Route.useLoaderData()
const { status } = Route.useSearch()
const router = useRouter()
async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
const title = String(new FormData(e.currentTarget).get('title') ?? '')
await addTask({ data: { title } })
router.invalidate() // re-run the loader
}
return (
<main>
<h1>Tasks ({status})</h1>
<form onSubmit={onSubmit}>
<input name="title" />
<button type="submit">Add</button>
</form>
<ul>{tasks.map((t) => <li key={t.id}>{t.title}</li>)}</ul>
</main>
)
}
A few things are happening here that are typical of how Start code reads:
- The URL is typed state.
validateSearchparses the query string with Zod. An invalid value such as?status=bogusfalls back toallvia.catch(), and the router redirects to the normalised URL. Elsewhere in the app,<Link to="/tasks" search={{ status: 'open' }}>is checked at compile time, so a typo in the route path or the status value is a type error. - Loader dependencies are explicit.
loaderDepstells the router which search params the loader depends on, so the loader re-runs and caches by those values. - Server functions validate at runtime. TypeScript types do not validate anything at runtime, and the Start vs Next.js page makes the same point about both frameworks. Passing a Zod schema to
.validator()means bad input from the network is rejected before your handler runs. - Mutations then invalidate. After
addTaskresolves,router.invalidate()re-runs active loaders. For larger apps the docs recommend the TanStack Query integration for caching and refetching instead.
Choosing how each route renders
Selective SSR is controlled by an ssr option on the route. true (the default) runs beforeLoad and the loader on the server and renders HTML. 'data-only' runs the data loading on the server but renders the component in the browser, which suits components that depend on browser-only APIs such as charts. false moves everything to the client. You can also pass a function that decides per request, and set defaultSsr in createStart to change the default for the whole app.
export const Route = createFileRoute('/reports')({
ssr: 'data-only',
loader: () => getReportData(),
component: ReportChart,
})
Upgrading from the release candidate
If you started a project on the RC, the official migration guide lists the changes for moving to @tanstack/react-start 1.169, the first stable release. The ones most likely to affect you:
- Vite 8 is required if you build with Vite. Follow Vite’s own migration guide first.
.inputValidator()is now.validator()on server functions and middleware, and it takes the same validator. Some docs pages and many tutorials still showinputValidator; on 1.169 the old name no longer exists on the type and your build will fail to type-check.@tanstack/nitro-v2-vite-pluginis gone. Usenitro()fromnitro/viteinstead.useSessionwas removed. The guide suggests a session library such asiron-session, and warns it cannot read existing Start session cookies. Either use a new cookie name and ask users to sign in again, or test a migration that reads the old cookies first.- Error and response handling changed. A server function call now rejects with whatever was thrown.
setResponseHeader('set-cookie', ...)replaces all outgoing cookies, so usesetCookieorappendResponseHeaderto add one. Uncaught errors return a generic JSON body with a 4xx/5xx status. If you have a custom server entry, read this section of the guide carefully.
None of these are large rewrites, but the cookie and session changes can log users out or drop cookies silently, so test authentication flows before deploying.
TanStack Start or Next.js?
TanStack publishes its own “Start vs Next.js” page, checked against Next.js 16.3. It is written by one of the two parties, but it is more balanced than most framework comparisons and even lists cases where Next.js is the better choice. Combined with the Next.js documentation, the main differences look like this:
| Area | TanStack Start 1.0 | Next.js (App Router) |
|---|---|---|
| Routing | TanStack Router: typed links, params, validated search params, loaders and context | File-system App Router; compile-time link checking via opt-in typedRoutes |
| Server Components | Experimental, opt-in | The default rendering model |
| Server code | Server functions (createServerFn) with inferred types |
Server Actions, integrated with forms and React’s pending/error APIs |
| Caching | Router loader cache (staleTime, gcTime), optional TanStack Query |
Cache Components, use cache, tag-based revalidation |
| Rendering control | Per-route selective SSR (true, 'data-only', false) |
Static, dynamic and streaming, decided by the caching model |
| Build tool | Vite or Rsbuild | Turbopack |
| Built-ins | Bring your own image and font handling | next/image, next/font, metadata conventions |
TanStack’s page makes no performance claims and says any benchmark should use the same app on the same hardware, which is good advice for any comparison.
Start is a strong fit when
- Your app is highly interactive, with lots of state in the URL (filters, tabs, pagination), and you want that state typed and validated.
- You already use TanStack Query or TanStack Router and want server rendering without changing your mental model.
- You want to stay on Vite and its plugin ecosystem, or you want to deploy to several hosts without relying on one platform’s features.
- You prefer a client-first model and would rather opt in to server rendering per route than reason about server and client component boundaries.
Next.js remains the safer choice when
- Your architecture relies on React Server Components today, as Start’s support is still experimental.
- You depend on
next/image,next/fontand the metadata API, since replacing them is real migration work. - You have a working Next.js app. The TanStack page itself says a different routing model alone is not a reason to rewrite.
- Your team needs the larger hiring pool, ecosystem and volume of documentation that comes with the most widely used React framework.
My recommendation for teams that are undecided follows TanStack’s own suggestion: build one representative route in Start that covers real data loading, search-param validation, a mutation, an unauthorised-access case and a deployment to your actual host. A day spent on that answers more questions than any comparison table, including this one.
The bottom line
TanStack Start 1.0 doesn’t introduce many new features compared with the RC. Its main change is the stability promise. The framework’s strengths are type-safe routing and URL state, server functions that are easy to follow, and per-route control over SSR. Its main gap compared with Next.js is Server Components, which TanStack treats as optional and still experimental. If that trade-off suits your app, Start is now a reasonable choice for production React projects. If you are on the RC, plan an afternoon for the upgrade, rename .inputValidator() to .validator(), and retest anything that touches sessions or cookies.
Sources: TanStack Start 1.0 announcement, TanStack Start overview, Quick Start, Build from Scratch, Server Functions, Selective SSR, Server Components guide, Migrate from the Release Candidate, TanStack Start vs Next.js, @tanstack/react-start on npm