file-based routing

[check: …]
📖 Read the full guide — the src/routes/ file convention, the codegen step, and every route kind ($param, _pathless, $splat). This page is the interactive companion.

The one idea

The filesystem IS the route tree: files under src/routes/ ARE routes, and a plugin compiles them into routeTree.gen.ts — the generated file that powers the type-safety. $ = a param, _ prefix = a pathless layout, and a layout route wraps its children through an <Outlet/>.

There is no hand-written route config to keep in sync. You author route files; the Vite plugin (@tanstack/router-plugin/vite) watches the directory and regenerates src/routeTree.gen.ts on every save. That generated file is imported by createRouter({ routeTree }) — and its inferred types flow out to every <Link>, navigate, useParams, and loader. It is generated, not hand-written — yet you commit it (it is app source, not a throwaway artifact).

file = route node __root.tsx = wraps all index.tsx = the "/" of its dir $param = dynamic segment _name = pathless layout $.tsx = splat / catch-all

The codegen step — files become a typed route tree

This is the half that makes TanStack Router different from a plain file-mapper: the files are compiled, not just read. Click run codegen to watch the plugin walk src/routes/ and emit src/routeTree.gen.ts, which the typed router then consumes.

1 · author
src/routes/*.tsx
// you write route files
__root.tsx
index.tsx
posts.tsx
posts/$postId.tsx
_layout/route-a.tsx
...
2 · codegen
@tanstack/router-plugin/vite
// tanstackRouter({ target:'react',
//   autoCodeSplitting:true })
watches src/routes/
emits routeTree.gen.ts
3 · generated
src/routeTree.gen.ts
// GENERATED — do not hand-edit
rootRoute
  .addChildren([...])
// + FileRoutesByPath type
//   (the type-safety is born here)
4 · runtime
createRouter + types OUT
// routeTree feeds the router;
// its types flow to Link,
// navigate, useParams, loader
Hit run codegen to watch files compile into the typed route tree.

The route-tree visualizer — click a file, see its route + nesting

Left: the curated src/routes/ tree (mirrors the official conventions). Right: type a URL (or click a sample) — the resolver shows which file matches, the params it receives, and the component chain it renders inside. Clicking a file in the tree jumps the other way. _layout is a pathless route: it wraps its children but adds nothing to the URL.

__root static / index $param $ splat _pathless

The component chain — where the matched route nests

A layout route renders an <Outlet/> where its child goes. So a matched URL renders as a nested chain from <Root> down to the leaf. Pathless layouts (the dashed _layout below) sit in the chain but contribute no URL segment — that is the entire point of the _ prefix.

Resolve a URL above to see its chain render here.

File convention → meaning → example URL

Two ways to make a layout route: a co-located file (posts.tsx next to a posts/ dir) or a route.tsx inside the directory (settings/route.tsx). Both wrap their directory's children. The _ prefix is different: it makes the route pathless (a layout with no URL segment of its own).

FileKindRoute path / URLRead as
__root.tsxroot route— (no URL; wraps everything)createRootRoute()
index.tsxindex/ (the root index)path '/'
about.tsxstatic/aboutpath 'about'
posts.tsxlayout route (co-located file)/posts + wraps posts/*path 'posts' + <Outlet/>
posts/index.tsxindex (of dir)/posts (exact)path '/' under posts
posts/$postId.tsxdynamic segment/posts/$postIdparams.postId (string)
settings/route.tsxlayout route (dir's route.tsx)/settings + wraps settings/*path 'settings' + <Outlet/>
settings/profile.tsxstatic nested/settings/profilepath 'profile' under settings
_layout.tsxpathless layout (_ prefix)— (no URL segment)id: '_layout', no path
_layout/route-a.tsxstatic, wrapped by pathless/route-a (no /_layout in URL)path 'route-a'
files/$.tsxsplat / catch-all/files/$ (any depth)params._splat (string)
posts_/$postId/edit.tsxnon-nested (trailing _)/posts/$postId/edit — NOT wrapped by Postsdetached from parent layout

A trailing _ on a name (posts_ dir, or posts. flat → posts_.) detaches the route from the parent's layout component while keeping the URL nested. Flat routes use . instead of /: posts.$postId.tsxposts/$postId.tsx.

Verified against tanstack.com/router · file-based routing + /installation/with-vite + /faq (routeTree.gen.ts is generated, commit it) and TkDodo — The Beauty of TanStack Router (secondary). Cross-refs: 🔗 router_type_safety (the codegen powers the types) · 🔗 astro_routing_layouts (same file-based idea, different framework) · 🔗 tanstack_start_overview.