I recently migrated a mid-sized SaaS application from Next.js App Router to TanStack Start. This post documents what I learned — the wins, the gotchas, and the honest trade-offs.
TL;DR: TanStack Start wins on type safety, developer ergonomics, and bundle size. Next.js has a larger ecosystem and more hosting options. For a TypeScript-first team building a complex SPA, TanStack Start is the better choice.
The Application Context
The app I migrated:
- 47 routes (including dynamic and nested)
- Authentication with session-based auth
- ~15 API endpoints (server functions)
- Heavy use of React Query for data fetching
- Deployed on Cloudflare Workers
This wasn't a toy app — it's a real SaaS with paying customers.
Mental Model Differences
Next.js: File = Route + Server Component + Data
Next.js blends routing, rendering, and data fetching into files. page.tsx becomes a React Server Component by default, and you opt into client with 'use client'. Data is fetched directly in components.
TanStack Start: Route = Config + Loader + Component
TanStack Start separates concerns more explicitly. Routes are defined with createFileRoute(), loaders handle data fetching, and components are always client components. Server functions (createServerFn()) handle server-side logic.
Neither approach is wrong — they're different mental models that suit different team preferences.
What Got Better
End-to-End Type Safety
This was the biggest win. In Next.js, I was constantly casting params from unknown and manually typing search parameters. In TanStack Start:
// Next.js — params are unknown, require casting
export default async function Page({ params }: { params: { slug: string } }) {
const post = await getPost(params.slug) // params.slug could be anything
}
// TanStack Start — fully typed, validated at runtime
export const Route = createFileRoute('/blog/$slug')({
loader: ({ params }) => getPost(params.slug), // params.slug: string, guaranteed
component: BlogPost,
})
Across 47 routes, this eliminated an entire category of runtime errors.
React Query Integration
Next.js and React Query coexist awkwardly. Next.js wants to own data fetching; React Query also wants to own data fetching. You end up with workarounds like initialData hydration that are fragile and confusing.
TanStack Router + TanStack Query is designed to work together:
export const Route = createFileRoute('/users')({
loader: ({ context }) =>
// Prefetch in loader, React Query handles the rest
context.queryClient.ensureQueryData(usersQueryOptions()),
component: UsersPage,
})
function UsersPage() {
// Data is already in cache from loader — no loading state on initial render
const { data } = useQuery(usersQueryOptions())
}
Bundle Size
After migration, our JavaScript bundle dropped by 34%. Next.js App Router ships a significant runtime. TanStack Start is leaner.
What Got Harder
Image Optimization
next/image is genuinely excellent. TanStack Start has no equivalent. You'll need to handle image optimization yourself — either through Cloudflare Images, a CDN, or a library like unpic.
Incremental Static Regeneration
If your app relies heavily on ISR for performance, that pattern doesn't have a direct equivalent in TanStack Start. You'll need to rethink your caching strategy using CDN cache headers or a dedicated caching layer.
Ecosystem
Some Next.js-specific libraries (next-auth, next-intl, etc.) don't work out-of-the-box. You need to find or build equivalents. For us, the TanStack ecosystem was sufficient, but if you depend on Next.js-specific plugins, audit them before migrating.
The Migration Process
Here's the approach that worked for us:
Phase 1: Extract business logic Move all business logic, API calls, and data utilities to framework-agnostic files. This took about a week and paid dividends immediately — it forced us to clean up a lot of coupling that had crept in.
Phase 2: Rebuild routes Recreate routes one by one in TanStack Start. Start with the simplest routes (no loaders, no params) and work toward complexity. This took two weeks.
Phase 3: Server functions Migrate Next.js API routes and Server Actions to TanStack Start server functions. This was mostly mechanical.
Phase 4: Authentication Rebuild auth using our chosen auth library. This was the riskiest phase — budget extra time for testing.
Total migration time: 5 weeks for a 2-developer team.
Would I Do It Again?
Yes — for this use case. The type safety improvements alone have prevented multiple production bugs. The developer experience is genuinely better for our team's workflow.
If you're building a content-heavy site that relies on ISR and image optimization, Next.js remains the better choice. If you're building a complex TypeScript application with lots of routes and data interactions, TanStack Start is worth the migration cost.