The Route Tree Mental Model

Lesson 1 — How flat filenames build nested routing

You know Next.js: a folder is a path segment, and layout.tsx wraps its children. TanStack Router flips this: filenames are the path, and dots create nesting. The trick is learning to read a flat file tree and see the route tree in your head.

From Folders to Filenames

In Next.js:

app/ ├── home/ │ ├── page.tsx → /home │ └── [id]/ │ └── page.tsx → /home/123 └── layout.tsx → wraps everything

In TanStack Router, the same idea is encoded in flat filenames with dots:

routes/ ├── home.tsx → /home (also layout) ├── home.$postId.tsx → /home/$postId └── __root.tsx → wraps everything
⚡ Key insight The dot in a filename is the parent–child relationship. home.dashboard.tsx is a child of home.tsx. No folder needed.

The <Outlet> = Where Children Render

In Next.js, layouts get {children} automatically. In TanStack Router, you explicitly choose where child routes render using <Outlet />.

// routes/home.tsx — a layout route
import { Outlet, createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/home')({
  component: HomeLayout,
})

function HomeLayout() {
  return (
    <div className="feed-layout">
      <nav>Feed • Explore • Profile</nav>
      <Outlet />  {/* ← child route renders here */}
    </div>
  )
}

When you visit /home/42, TanStack Router finds both home.tsx and home.$postId.tsx, renders the layout, and plugs the child into the outlet:

URLComponent Tree
/home<HomeLayout>
/home/42<HomeLayout><PostDetail postId="42" /></HomeLayout>

Building a Mobile-Like App

Let's model a typical mobile social app: tabs at the bottom, detail screens that slide in, and settings tucked behind a layout.

routes/ ├── __root.tsx → always rendered ├── _app.tsx → / (pathless layout, holds tabs) ├── _app.index.tsx → / (home feed) ├── _app.explore.tsx → /explore ├── _app.profile.tsx → /profile ├── _app.posts.$postId.tsx → /posts/42 └── _app.posts.$postId.comments.tsx → /posts/42/comments
⚡ The underscore prefix _app.tsx is a pathless layout. It wraps children but does not add a segment to the URL. The URL is /explore, not /_app/explore. We'll cover this fully in Lesson 2.

How it composes

URLRendered Tree
/<Root><AppLayout><HomeFeed /></AppLayout></Root>
/explore<Root><AppLayout><Explore /></AppLayout></Root>
/posts/42<Root><AppLayout><PostDetail postId="42" /></AppLayout></Root>
/posts/42/comments<Root><AppLayout><PostDetail postId="42"><Comments /></PostDetail></AppLayout></Root>

Notice how /posts/42 renders <PostDetail /> inside the app layout. But /posts/42/comments renders <Comments /> inside <PostDetail /> — which is itself inside <AppLayout />. Three levels of nesting, entirely from filenames.

Quick Quiz

1. What URL does routes/shop.items.$itemId.tsx match?

/shop
/shop/items/42
/shop/items/$itemId
The $ marks a dynamic segment. shop.items creates two static segments. The file maps to /shop/items/:itemId.

2. Which layout wraps _app.settings.tsx?

None — _app hides the layout
_app.tsx (the pathless layout)
__root.tsx only
The underscore prefix means "pathless", not "no layout". _app.tsx is still a layout route. Any file starting with _app. is its child.

3. If home.tsx has no <Outlet />, what happens at /home/dashboard?

Only <Dashboard /> renders
<HomeLayout /> renders, but child content is lost
404 error
TanStack still matches and renders home.tsx, but without an <Outlet /> there is nowhere to place the child. The child simply doesn't appear.
← Reference: Naming Cheat Sheet Lesson 2: Pathless Layouts & Groups →
🎓 Stuck on something?
Ask your teacher anything — route matching edge cases, why your tree isn't rendering as expected, or how this maps to your specific app. No question is too small.