Skip to content
October 26, 2026 Practical guides on software, AI and security

Search Shartech Blogs

Software Development

TanStack Start 1.0 Is Stable: What It Is, How to Upgrade from the RC, and When to Choose It Over Next.js

Table of Contents

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. validateSearch parses the query string with Zod. An invalid value such as ?status=bogus falls back to all via .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. loaderDeps tells 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 addTask resolves, 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 show inputValidator; on 1.169 the old name no longer exists on the type and your build will fail to type-check.
  • @tanstack/nitro-v2-vite-plugin is gone. Use nitro() from nitro/vite instead.
  • useSession was removed. The guide suggests a session library such as iron-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 use setCookie or appendResponseHeader to 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/font and 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

Related reading

Did you find this article helpful?

Written by

Malik Shamir

Malik Shamir is the founder and lead tech writer at SharTech, a modern technology platform focused on artificial intelligence, software development, cloud computing, cybersecurity, and emerging digital trends. With hands-on experience in full-stack development and AI systems, Shamir creates clear, practical, and research-based content that helps readers understand complex technologies in simple terms. His mission is to make advanced tech knowledge accessible, reliable, and useful for developers, entrepreneurs, and digital learners worldwide.

55 Articles Website
Previous Article Deno Is Joining Cloudflare: What Ends, What Stays, and How to Plan Your Migration

Leave a Comment

Your email address will not be published. Required fields are marked *

Stay Updated with Shartech

Get smart tech insights, tutorials, and the latest in AI & programming directly in your inbox. No spam, ever.

We respect your privacy. Unsubscribe at any time.