DEV Community

Cover image for TanStack Start vs Next.js: A Routing Cheat Sheet (for TanStack Router Devs Learning Next.js)

TanStack Start vs Next.js: A Routing Cheat Sheet (for TanStack Router Devs Learning Next.js)

If you already know TanStack Router and you're picking up Next.js App Router, most of routing is just a vocabulary swap. This post is the cheat sheet I wish I had — a straight mapping between the two, plus the handful of things that actually matter once you go beyond routing basics.

The Core Mapping

Concept TanStack Start Next.js App Router
Routes directory src/routes/ app/
Home src/routes/index.tsx app/page.tsx
Static route (/about) src/routes/about.tsx app/about/page.tsx
Nested route src/routes/dashboard/settings.tsx app/dashboard/settings/page.tsx
Dynamic route $postId.tsx [postId]/page.tsx
Root layout __root.tsx app/layout.tsx
Outlet for children <Outlet /> {children}
Loading UI pendingComponent loading.tsx
Error UI errorComponent error.tsx
Not found notFoundComponent / notFound() not-found.tsx / notFound()
Route params Route.useParams() useParams() or server params
Search params Route.useSearch() useSearchParams()
Link <Link to="/about" /> <Link href="/about" />
Programmatic nav useNavigate() useRouter()
Redirect redirect() redirect()
Route guards beforeLoad middleware / server checks
Data loading loader Server Components / fetch
Server functions Start server functions Server Actions / Route Handlers
Type-safe routing Built-in, excellent Weaker, no first-class typing
Search param validation Built into router Usually a library (e.g. Zod)

Folder Structure, Side by Side

TanStack Start:

src/
└── routes/
    ├── __root.tsx
    ├── index.tsx
    ├── about.tsx
    └── dashboard/
        ├── route.tsx
        ├── index.tsx
        └── settings.tsx
Enter fullscreen mode Exit fullscreen mode

Next.js:

app/
├── layout.tsx
├── page.tsx
├── about/
│   └── page.tsx
└── dashboard/
    ├── layout.tsx
    ├── page.tsx
    └── settings/
        └── page.tsx
Enter fullscreen mode Exit fullscreen mode

Quick translation table for the folder structure:

__root.tsx        →  layout.tsx
index.tsx         →  page.tsx
Outlet            →  children
$slug.tsx         →  [slug]/page.tsx
loader            →  Server Component data fetching
beforeLoad        →  middleware / server checks
<Link><Link>
useNavigate()     →  useRouter()
notFound()        →  notFound()
pendingComponent  →  loading.tsx
errorComponent    →  error.tsx
Enter fullscreen mode Exit fullscreen mode

Same Route, Two Mental Models

TanStack Router — you define a route object with lifecycle hooks:

export const Route = createFileRoute('/products/$id')({
  loader: ({ params }) => getProduct(params.id),
  component: ProductPage,
})
Enter fullscreen mode Exit fullscreen mode

Next.js App Router — the filesystem is the route tree, and the component itself fetches data:

// app/products/[id]/page.tsx
export default async function ProductPage({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  const product = await getProduct(id)
  return <Product product={product} />
}
Enter fullscreen mode Exit fullscreen mode

That's the real mental shift: TanStack Router thinks in router + route objects. Next.js thinks in filesystem + Server Components.

If you're coming from TanStack Start, spend your energy on that list — routing itself you already basically know.

Top comments (0)