Edge Kit launch!-$50 off
home

TanStack Start vs Next.js for SaaS (2026): Honest Tradeoffs

ยท18 min read

TanStack Start vs Next.js for SaaS in 2026: routing, server functions, caching, RSC, Cloudflare, auth, and when each wins, from a team shipping both.

Most TanStack Start vs Next.js threads are written by people who picked one and are defending it. We maintain two production SaaS starters, one on each: Core Kit runs Next.js 16 next to an Expo mobile app and a browser extension, and Edge Kit runs TanStack Start on Cloudflare Workers with D1, R2, and Queues. This post shows what each framework costs you on a real SaaS (auth, billing, background jobs, caching, deploys) and when we would pick each one.

Short answer

Pick Next.js if you want the biggest ecosystem, mature React Server Components and Server Actions, first-class Vercel hosting, the easiest hiring, or a web app that shares a backend with a React Native mobile app. Pick TanStack Start if you want a fully type-safe router (including search params), explicit server functions instead of framework-level caching, Vite DX, and a small runtime that deploys cleanly to Cloudflare Workers or anywhere else. For a web-only SaaS on Cloudflare we ship TanStack Start (Edge Kit). For web plus mobile we ship Next.js (Core Kit).

What is TanStack Start?

TanStack Start is a full-stack React framework built on TanStack Router. It adds full-document SSR, streaming, server functions (type-safe RPCs between client and server), server routes for raw HTTP, and middleware, and it builds with Vite or Rsbuild (TanStack Start overview). The router does most of the work. Start makes it full-stack.

Where it stands as of 9 October 2026:

  • Version: @tanstack/react-start is published as 1.168.x on npm (1.168.60 was published on 30 September 2026, per the npm registry). The official docs still call it a Release Candidate: "feature-complete and its API is considered stable" (overview). The v1 RC announcement from September 2025 has not been updated with a final 1.0 note yet. Our reading: the API is stable enough to build on, but pin your versions.
  • React Server Components: available, but experimental and off by default. You opt in with @vitejs/plugin-rsc and rsc: { enabled: true }, and the docs say RSC "will remain so into early v1" (Server Components guide).
  • Hosting: official partners are Cloudflare, Netlify, and Railway. Documented targets also include Vercel and Node/Docker (both through Nitro), Bun, and Appwrite Sites (hosting guide).

What Next.js looks like in 2026

Next.js is the default React framework for a reason, and version 16 fixed several long-standing complaints:

  • Turbopack is the default bundler for dev and build since Next.js 16 (Next.js 16).
  • proxy.ts replaces middleware.ts and runs on the Node.js runtime. middleware.ts still works for Edge runtime cases but is deprecated (Next.js 16).
  • Cache Components ("use cache") make caching opt-in. Next.js 16.4 (6 October 2026) recommends Cache Components for every app, turns them on by default in new create-next-app projects, and says they become the default in Next.js 17 (Next.js 16.4).
  • The Adapter API is stable since 16.2. Platforms get a typed description of the build output instead of reverse-engineering it (Next.js across platforms).

The opt-in caching model removes the biggest reason people left the App Router. It also means a lot of 2024-era "Next.js caches everything behind your back" arguments are out of date. If someone compares TanStack Start to Next.js 14 caching, they are comparing against the wrong framework.

TanStack Start vs Next.js: comparison table

DimensionNext.js 16TanStack Start
RoutingFile-based App Router, layouts, parallel/intercepting routesFile-based TanStack Router with generated, fully typed routes, params, and search params
Data loadingAsync Server Components, fetch, Server ActionsRoute loaders + server functions, usually paired with TanStack Query
Server codeServer Actions ("use server"), route handlerscreateServerFn() with middleware and validators, server routes for raw HTTP
RSCCore of the App Router, matureExperimental, opt-in
CachingCache Components: "use cache", cacheLife, updateTagNo framework data cache. TanStack Query on the client, HTTP headers and your own KV/CDN on the server
Build toolTurbopack (webpack opt-in)Vite (or Rsbuild)
HostingVercel first-class; Node, Docker, and others via adapters/OpenNextCloudflare Workers, Netlify, Railway, Vercel, Node, Bun via plugins or Nitro
Auth (Better Auth)Supported, nextCookies() pluginSupported, tanstackStartCookies() plugin
EcosystemLargest: SDK quickstarts, templates, answers, hiring poolSmaller but growing fast; strong TanStack Query/Table/Form fit
Learning curveServer/client component boundary, caching model, many conventionsSmaller mental model; you need to know the router and Query well
Mobile storyPair with Expo through a shared API in a monorepoSame is possible, but few starters do it

The rest of this post covers the four places where that table turns into daily work: server code, routing, caching, and deployment.

Server code: Server Actions vs createServerFn

This is the difference you will feel most often. Both frameworks let a React component call server code without hand-writing a fetch client. They differ in how explicit that call is.

app/projects/actions.ts
"use server";

import { updateTag } from "next/cache";
import { headers } from "next/headers";
import * as z from "zod";

import { db } from "@/db";
import { project } from "@/db/schema";
import { auth } from "@/lib/auth";

const input = z.object({ name: z.string().trim().min(1).max(100) });

export async function createProject(formData: FormData) {
  const session = await auth.api.getSession({ headers: await headers() });
  if (!session) throw new Error("Unauthorized");

  const { name } = input.parse({ name: formData.get("name") });

  await db.insert(project).values({ name, userId: session.user.id });
  updateTag(`projects:${session.user.id}`);
}

<form action={createProject}> works with or without client JavaScript, and updateTag gives read-your-writes for a "use cache" segment tagged with that key (Next.js 16).

Notes from shipping both:

  1. Both are public endpoints. A Server Action and a server function are both reachable without your UI. The TanStack docs say it plainly: beforeLoad is route UX, not the data boundary. Put the session check in the function, every time.
  2. TanStack Start ships CSRF protection for server functions. If you don't define src/start.ts, Start installs createCsrfMiddleware() for you. If you do define it, you have to add the middleware yourself (server functions guide).
  3. API names moved during the RC. Current TanStack and Cloudflare docs use .validator(). Some third-party guides, including Better Auth's, show .inputValidator(). Check the version you install.
  4. Raw HTTP looks similar in both. A Stripe webhook is export async function POST(request: Request) in a Next.js route.ts, and a server.handlers.POST on a createFileRoute("/api/webhooks/stripe") in TanStack Start. Both receive a standard Request and return a Response.

Core Kit does not route most mutations through Server Actions. It uses a Hono API mounted as a Next.js route handler, because the same endpoints have to serve the Expo app and the browser extension. If you only have a web client, Server Actions and server functions are both fine.

Routing and search params

TanStack Router is the strongest reason teams switch. Routes, path params, and search params are typed end to end, and links to routes that don't exist fail at compile time.

src/routes/dashboard/projects.tsx
import { createFileRoute } from "@tanstack/react-router";
import * as z from "zod";

export const Route = createFileRoute("/dashboard/projects")({
  validateSearch: z.object({
    page: z.number().int().min(1).catch(1),
    status: z.enum(["active", "archived"]).catch("active"),
  }),
  component: Projects,
});

function Projects() {
  const { page, status } = Route.useSearch(); // number, "active" | "archived"
  // <Link to="/dashboard/projects" search={{ page: 2, status: "archived" }} /> is type-checked
}

In Next.js 16, searchParams is an async prop of loosely typed strings (await searchParams is now required, per the Next.js 16 breaking changes). You can add Zod parsing or a library like nuqs, but you are adding it yourself. On a SaaS dashboard with filters, tables, pagination, and saved views, URL state is everywhere, and this difference adds up.

What Next.js has that TanStack Router doesn't copy one-to-one: parallel and intercepting routes (modal-over-page patterns), RSC-aware layouts, and the Metadata API with file conventions for OG images and sitemaps. TanStack Start covers SEO with a route-level head() and static prerendering, which is enough for a marketing site plus an app.

Caching and rendering: explicit vs managed

This is where the two frameworks really differ in philosophy.

Next.js manages caching for you, now on an opt-in basis. With Cache Components you mark a component or function with "use cache", set a cacheLife profile, and invalidate with updateTag or revalidateTag. Combined with Partial Prerendering, you get a static shell with dynamic holes, and instant client navigations. That is real value for content-heavy and personalized pages, and no other React framework does it as well today.

TanStack Start leaves caching to you. There is no framework data cache. Typically:

  • TanStack Query caches on the client, and route loaders prefetch into it.
  • HTTP headers set from a server function (setResponseHeaders) let browsers and CDNs cache public responses. The docs include a pointed warning: never send public on a response that depends on a session (server functions guide).
  • A server-side store such as Workers KV holds expensive shared values. Edge Kit wraps KV and documents the cache-aside pattern in its caching guide.

For a logged-in SaaS dashboard, most data is per-user and shouldn't be shared-cached anyway. That is why we like the explicit model there: one fewer layer to debug when a customer says "I saved it but it didn't change." For a marketing site with thousands of programmatic pages, Next.js's model does more of the work for you.

Running TanStack Start on Cloudflare Workers

TanStack Start runs on Cloudflare Workers through the official Cloudflare Vite plugin, and Cloudflare is one of its three official hosting partners. Setup is one plugin in vite.config.ts and a wrangler.jsonc whose main points at @tanstack/react-start/server-entry (Cloudflare's TanStack Start guide):

vite.config.ts
import { cloudflare } from "@cloudflare/vite-plugin";
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [
    cloudflare({ viteEnvironment: { name: "ssr" } }),
    tanstackStart(),
    react(),
  ],
});

Why this pairing works so well for a SaaS:

  • Dev runs on the real runtime. The Vite plugin runs your Worker locally, so D1, KV, R2, and Queues are simulated with the same bindings you deploy. There is no "works on Node, breaks on the edge" gap.
  • Bindings are plain imports. import { env } from "cloudflare:workers" inside a server function gives you env.DB (D1), env.UPLOADS (R2), env.KV, or env.AI. You don't manage connection strings or pools.
  • Background jobs live in the same Worker. A custom server entry exports fetch: handler.fetch next to queue() and scheduled() handlers, and can export Durable Objects and Workflows too (custom entrypoints).
src/server.ts
import handler from "@tanstack/react-start/server-entry";

export default {
  fetch: handler.fetch,
  async queue(batch, env, ctx) {
    for (const message of batch.messages) {
      // send the delayed welcome email, generate the report, etc.
      message.ack();
    }
  },
} satisfies ExportedHandler<Env>;

That is the shape of Edge Kit: one Worker containing the TanStack Start app, Better Auth, Stripe webhooks, a Queues consumer for background jobs, R2 uploads, and Workers AI, with Drizzle on D1. The deployment guide builds, applies D1 migrations, and publishes with a single pnpm deploy, and Cloudflare's GitHub integration gives you preview URLs on pull requests.

Be honest about D1 before you commit. Each D1 database is capped at 10 GB, and that limit cannot be raised. Each database is single-threaded, so throughput depends on how long your queries take (D1 limits). That is plenty for most early SaaS products, and Cloudflare designs D1 for many small per-tenant databases. But if you already know you need one large Postgres, connect it through Hyperdrive (Edge Kit's Hyperdrive recipe) or reconsider the stack.

Next.js can run on Cloudflare too, through OpenNext. We document that path for Core Kit in deploy TurboStarter to Cloudflare Workers, with Hyperdrive for Postgres and R2 for the incremental cache. It works well, but it is an adapter layer over a framework designed around Vercel's model. TanStack Start on Workers has no translation layer: Vite builds a Worker, and that's it.

When Next.js is still the better pick

We ship TanStack Start, and we would still choose Next.js in these cases:

  1. You want the largest ecosystem. Most SaaS vendors publish a Next.js quickstart first (auth, analytics, error tracking, CMS, payments). With TanStack Start you will more often adapt a generic React or Vite guide.
  2. You want RSC and Server Actions in production today. They are the core of the App Router and mature. In TanStack Start, RSC is experimental.
  3. You deploy to Vercel. Next.js on Vercel is the most integrated path in the React world: previews, image optimization, ISR, and Cache Components all work without configuration.
  4. You are hiring. "Next.js" on a job post brings in far more candidates who already know the framework. TanStack Start experience is still rare.
  5. You have a mobile app on the roadmap. A Next.js web app, an Expo app, and a shared API in one Turborepo is a well-trodden path (more on this below).
  6. You need Postgres and long-running Node workloads. If the product needs a big relational database, long-lived connections, heavy server CPU, or Node-only libraries, a Node server or serverless Node functions are simpler than fitting the work into Workers.
  7. You need content-heavy pages at scale. Partial Prerendering plus "use cache" is excellent for big marketing sites, docs, and programmatic SEO.
  8. You want answers fast. There are more docs, forum answers, blog posts, and public example repos for Next.js, so you get unstuck faster on edge cases.

When TanStack Start wins

  1. Your app is mostly a dashboard. Type-safe routes and search params make filter-heavy UIs safer to refactor.
  2. You want to see where server code runs. createServerFn plus middleware is just functions: no "use server" boundary rules or serialization surprises across component trees.
  3. You don't want framework caching. Nothing is cached unless you cache it. TanStack Query is the cache most React devs already know.
  4. You want Vite DX. Fast HMR, the Vite plugin ecosystem, and Vitest share one config.
  5. You want to deploy anywhere. Cloudflare Workers, Netlify, Railway, Node, Bun, or Vercel, without the framework assuming one host.
  6. You are going all-in on Cloudflare. D1, R2, KV, Queues, Workers AI, and Durable Objects become imports, and you pay for one platform.
  7. You want a smaller mental model. One router, one data library, explicit server functions. New developers on our Edge Kit codebase get productive quickly once they know TanStack Router.

TanStack Start authentication with Better Auth

Better Auth works on both frameworks, so authentication shouldn't decide this choice. On TanStack Start you mount auth.handler on a catch-all server route at /api/auth/$ and add the tanstackStartCookies() plugin (last in the plugin array) so server-side sign-in calls can set cookies (Better Auth TanStack Start integration). Then you check the session in beforeLoad for redirects and in server function middleware for data access. On Next.js the equivalent is a toNextJsHandler(auth) route plus the nextCookies() plugin, with optional checks in proxy.ts (Better Auth Next.js integration).

Edge Kit ships exactly that, with the Drizzle SQLite adapter on D1: email and password with verification, Google, GitHub, and Cloudflare OAuth, anonymous sessions, Turnstile on auth forms, and Workers rate limiting (Edge Kit auth overview). Core Kit uses Better Auth on Next.js with Postgres and adds magic links, 2FA, passkeys, and organizations with RBAC. If you are still choosing an auth provider, our Better Auth vs Clerk vs NextAuth comparison covers that decision.

What we'd pick for a SaaS with a mobile app

Next.js, inside a monorepo with Expo. That is why Core Kit exists: a Next.js 16 web app, an Expo app, and a WXT browser extension share a Hono API, Better Auth, and Drizzle + Postgres through @workspace/* packages. The mobile app talks to the same API the web app uses, so one auth system, one billing model, and one database serve all three clients.

You could build the same thing around TanStack Start, but you would be building it yourself. Server functions are designed to be called by your Start app (the docs point you to server routes for anything external), so a mobile client needs an explicit HTTP API either way. Next.js also has far more prior art for Expo monorepos. Edge Kit is deliberately web-only.

If you want to compare starters by framework, MakerKit and supastarter both offer TanStack Start editions next to Next.js; see TurboStarter vs MakerKit, or the wider Next.js SaaS boilerplate roundup.

Decision rules

Run these in order. Stop at the first one that matches.

  1. Mobile or browser extension planned within a year? Next.js in a monorepo with a shared API.
  2. The team already ships Next.js and nothing hurts? Stay on Next.js 16 and adopt Cache Components. A rewrite rarely pays for itself.
  3. Hosting on Vercel? Next.js.
  4. Hosting on Cloudflare, data fits D1 (or Hyperdrive to Postgres)? TanStack Start.
  5. App is mostly an authenticated dashboard with heavy URL state? TanStack Start.
  6. Big content or programmatic SEO surface with personalization? Next.js.
  7. You depend on a vendor SDK that only documents Next.js? Next.js, or budget time to adapt it.
  8. Still tied? Build the riskiest screen of your product in both over a weekend. Pick the one you didn't fight.

Frequently asked questions

Sounds good?Now let's make it real. In minutes.
Try TurboStarter

Verdict: TanStack Start vs Next.js for SaaS

TanStack Start vs Next.js is no longer a contest between a stable framework and an experiment. Next.js 16 fixed its caching story and still has the deepest ecosystem. TanStack Start is a stable-API, type-safe, explicit framework that works especially well on Cloudflare.

  • Choose Next.js for ecosystem, RSC maturity, Vercel, hiring, content-heavy SEO, and any SaaS that shares a backend with a mobile app. That is Core Kit.
  • Choose TanStack Start for typed routing, explicit server functions, Vite, and a single-Worker deploy on Cloudflare with D1, R2, and Queues. That is Edge Kit, and the Edge Kit stack docs show how the pieces fit.

Either way, the framework is the cheaper decision. Auth, billing, jobs, and deploys are where the weeks go, so pick a framework your team won't fight and spend the time on the product.

world map
Community

Connect with like-minded people

Join our community to get feedback, support, and grow together with 1,000+ builders on board, let's ship it!

Join us

Ship your startup everywhere. In minutes.

Don't burn tokens on setup and start building features on day one.

Get TurboStarter