Skip to content

File-Based vs Code-Based Routing in TanStack Router: Which Is Actually Easier?

I spent three hours trying to set up TanStack Router’s file-based routing, only to realize I picked the wrong approach for my project size.

The Problem: Why TanStack Router Feels Complicated

I was migrating from React Router to TanStack Router and assumed file-based routing was the “modern” way to go. After all, it’s prominently featured in the documentation. But here’s what I ran into:

Error: Could not find route tree generator
at createRouteTree (router-core.js:234)
at buildRouteTree (vite-plugin.js:89)

The Vite plugin wasn’t generating my routes correctly. I had mismatched file naming conventions, forgot to configure the plugin properly, and spent way too much time debugging build-time transformations.

Then I found this comment on Reddit:

“Are you using file based or code based routing? For me, code based routing was a breeze to setup” — hyperaeolian (score 13)

Wait, there’s another option?

The Solution: Choose the Right Routing Approach

Turns out, TanStack Router offers two completely different routing paradigms:

ApproachBest ForSetup ComplexityScaling
Code-basedSmall to medium projectsLowManual
File-basedLarge projects (50+ routes)HighAutomatic

Code-Based Routing: The Easier Path

Here’s what code-based routing looks like:

routes.tsx
import {
createRouter,
createRoute,
createRootRoute,
} from '@tanstack/react-router'
import { RootLayout } from './components/RootLayout'
import { Home } from './pages/Home'
import { About } from './pages/About'
import { Users } from './pages/Users'
// Define routes explicitly
const rootRoute = createRootRoute({
component: RootLayout,
})
const indexRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/',
component: Home,
})
const aboutRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/about',
component: About,
})
const usersRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/users',
component: Users,
})
// Build the tree
const routeTree = rootRoute.addChildren([
indexRoute,
aboutRoute,
usersRoute,
])
// Create router
const router = createRouter({ routeTree })
export default router

No Vite plugin. No build-time magic. Just explicit route definitions that I can inspect and debug directly.

Switching to this approach took me 15 minutes. I had my app running immediately.

File-Based Routing: Better at Scale

File-based routing isn’t bad—it’s just designed for different use cases:

File structure
src/
routes/
__root.tsx # Root layout
index.tsx # -> /
about.tsx # -> /about
users/
index.tsx # -> /users
$id.tsx # -> /users/:id (dynamic)
profile.tsx # -> /users/profile

Each file becomes a route automatically. But this requires:

vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { TanStackRouterVite } from '@tanstack/router-vite-plugin'
export default defineConfig({
plugins: [
TanStackRouterVite(), // Must be before React plugin
react(),
],
})

And understanding conventions like:

  • $param for dynamic segments
  • _layout for layout routes (invisible in URL)
  • - prefix for unauthenticated routes

Why This Matters: Trade-offs Between Approaches

I made the classic mistake: assuming the “modern” default was right for my use case.

Decision flow
Project Size?
┌───────────────┼───────────────┐
│ │ │
Small Medium Large
(<10 routes) (10-30 routes) (50+ routes)
│ │ │
v v v
Code-based Code-based File-based
Easy Easy Complex
Manual Manual Automatic
Setup Setup Discovery

The Unfair Comparison

As another Reddit commenter pointed out:

“Many users compare TanStack Router’s file-based routing to React Router’s standard routing, when the fair comparison would be to React Router’s framework mode.” — Dethstroke54

React Router’s framework mode also uses file-based conventions. It’s not that TanStack Router is more complex—it’s that file-based routing inherently requires more setup.

Common Mistakes: What I Got Wrong

  1. Assuming file-based is always “modern”: File-based routing optimizes for large-scale DX, not setup speed.

  2. Not reading the routing docs carefully: I skipped to the file-based section because it came first in the docs. Code-based routing was buried in the “Guides” section.

  3. Ignoring project size: I was building a simple portfolio site with 5 routes. File-based routing was overkill.

  4. Comparing across frameworks unfairly: I compared TanStack file-based to React Router code-based. That’s apples to oranges.

When to Actually Use File-Based Routing

File-based routing shines when you have:

  • 50+ routes in a single application
  • Multiple developers needing route discoverability
  • Nested layouts that benefit from file co-location
  • A need for automatic type generation

As one developer noted:

“I prefer file-based routing for simplicity in my custom mdx framework—create a file and get a route.” — Xacius

That “create a file, get a route” experience is powerful for content-heavy applications.

Migration Path

The good news: you can start with code-based and migrate to file-based later.

Migration strategy
Phase 1: Code-based (Week 1)
- Quick setup
- Learn TanStack Router concepts
- Ship features fast
Phase 2: Evaluate (Month 2-3)
- Count your routes
- Assess team pain points
- Consider file-based if >30 routes
Phase 3: File-based (Optional)
- Migrate incrementally
- One route file at a time
- Test thoroughly

Bottom Line

If you’re struggling with TanStack Router setup, switch to code-based routing. It’s explicitly designed to be simple:

  • No plugins required
  • No build-time magic
  • Direct control over your route tree
  • Easy debugging with standard tools

You can always adopt file-based routing later when your application actually needs it.


Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments