tanstack-router
Skill4 starsChanged 20 days agoMITView on GitHub
TanStack Router v1 for React — file-based routing, type-safe navigation, validated search params, loaders and router context. Use when working in a project that uses `@tanstack/react-router` — routeTree.gen.ts, createFileRoute, Link/useNavigate, validateSearch, loaderDeps, route masking, or code splitting.
14 other projects use a version of this.
Adopted by
Most recently taken up by
- yuan-alex/cedartheir own version, first seen Nov 8, 2026
- louisbrulenaudet/monorepo-templatetheir own version, first seen Oct 29, 2026
- LaoitdevOpen/laoitdev-skillstheir own version, first seen Oct 20, 2026
- itsLeonB/agent-config-managertheir own version, first seen Oct 20, 2026
- malinskibeniamin/skillstheir own version, first seen Oct 17, 2026
- rockcookies/skillstheir own version, first seen Oct 13, 2026
Used with
Projects with it also use accessibility, review and 1 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
ElasticSkillaccessibility
Accessibility guidance for Kibana. Use this skill when working with or reviewing EUI components, resolving a11y-related (@elastic/eui) ESLint issues, and ensuring proper use of ARIA attributes, focus management, keyboard interactions, and accessible naming conventions.
Also in 3 projects that use this
SentrySkillreview
Fixture review skill.
Also in 4 projects that use this
GoogleSkillimprove-codebase-architecture
Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.
Also in 3 projects that use this
Other versions14 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 14
- Mindrally/skills★ 275same name
- TangleML/tangle-ui★ 104same name
- louisbrulenaudet/monorepo-template★ 19same name
- malinskibeniamin/skills★ 4same name
- dvaJi/argos★ 3same name
- Spardutti/claude-skills★ 3same name
- Aver005/agent-skills★ 2same name
- bm629/agent-skills★ 2same name
- lobomfz/skills★ 2same name
- zkmake/skills★ 2same name
4 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository39 other files it gives its agents.
- AGENTS.mdAGENTS.md
- authoringSkill
- chrome-devtoolsSkill
- gamificationSkill
- imagegenSkill
- improve-architectureSkill
- instructionsSkill
- monorepoSkill
- reverse-engineerSkill
- runbookSkill
- security-reviewSkill
- setup-harnessSkill
- skillsSkill
- unslopSkill
- video-reportSkill
- youtube-transcriptSkill
- code-reviewSkill
- debugSkill
- interviewSkill
- prototypeSkill
- researchSkill
- workSkill
- adsenseSkill
- astryxSkill
- cloudflare-cliSkill
- dokploy-best-practicesSkill
- extract-themeSkill
- nextjsSkill
- payload-cmsSkill
- shadersSkill
- swrSkill
- tanstack-querySkill
- tanstack-startSkill
- tanstack-storeSkill
- tauriSkill
- vite-plusSkill
- web-assets-optimizationSkill
- web-extensionSkill
- zodSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
16 rules. 0 are common, 1 is distinctive, and 15 are very specific to this project.
0
Common
Most of the 11 edited versions we read keep these.
1
Distinctive
This author's own choices. Worth a look.
15
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 18
Prefer the project's existing route layout, validation library, and data layer.
- Very specific: line 45
Check whether routes come from the file-based generator (routeTree.gen.ts plus the bundler plugin) or from code-based createRoute calls.
- Very specific: line 48
Check the bundler config for tanstackRouter({ autoCodeSplitting: true }) — it decides whether .lazy.tsx files are needed at all.
- Very specific: line 50
If a queryClient sits in the router context, treat the router as a coordinator, not a cache (tanstack-query.md).
- Very specific: line 80
Every non-root route is created with createFileRoute, whose single string argument is written and maintained by the generator.
- Very specific: line 134
Every navigation is relative and has both an origin (from) and a destination (to).
- Very specific: line 146
Never interpolate values into to.
- Very specific: line 152
Prefer <Link> for anything clickable — it renders a real <a href>, so cmd/ctrl-click and middle-click work.
- Very specific: line 207
Write search params with <Link search={prev => ...}> or navigate({ search }).
- Very specific: line 226
Search params are deliberately absent from loader arguments.
- Very specific: line 255
After a load failure, prefer router.invalidate() over reset(): it reruns the loader and resets the boundary together.
- Very specific: line 261
Only routes that render an <Outlet /> can host a not-found boundary, so give the root route a notFoundComponent or the router a …
- Very specific: line 273
Prefer autoCodeSplitting: true in the bundler plugin.
- Very specific: line 278
When a component lives outside its route file, reach for getRouteApi('/path') instead of importing the Route object — same typed hooks, no …
- Very specific: line 287
No value is interpolated into to.
- Very specific: line 289
Every route reading query state defines validateSearch, with fallbacks for malformed input and an adapter where the validation library …
“Common” is measured: kept by at least 6 in 10 of the 11 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
TanStack Router
Reference for TanStack Router v1 with React. This skill covers client-side
routing only. For the full-stack framework built on top of this router —
server routes, server functions, SSR wiring, deployment — use the sibling
tanstack-start skill. Everything here still applies inside a Start app: Start
uses this router underneath.
Prefer the project's existing route layout, validation library, and data layer; apply these rules on top so route identity, type inference, and cache keys stay predictable.
Branch-specific references, loaded on demand:
- file-routing.md — naming conventions, the generated route tree, layouts, groups, splat and optional params.
- search-params.md —
validateSearch, schema adapters, search middlewares, reading and writing search state. - data-loading.md — loaders,
loaderDeps, caching options, router context, pending/error/not-found components, deferred data. - navigation.md —
Link,useNavigate, relative paths,linkOptions, preloading, route masking. - tanstack-query.md — router as coordinator, the SSR query integration, and the split of responsibilities with TanStack Query.
First Checks
- Confirm
@tanstack/react-routeris installed and read the lockfile version. This skill targets v1. - Find the router instance (usually
src/router.tsx). ReadcreateRouterfordefaultPreload,defaultPreloadStaleTime,defaultStaleTime,notFoundMode, androuteMasks— these change the correct advice for loaders and links. - Confirm the
Registerdeclaration merging block exists. Without it, top-levelLink,useNavigate, anduseSearchlose all route types. - Check whether routes come from the file-based generator (
routeTree.gen.tsplus the bundler plugin) or from code-basedcreateRoutecalls. Never editrouteTree.gen.tsby hand. - Check the bundler config for
tanstackRouter({ autoCodeSplitting: true })— it decides whether.lazy.tsxfiles are needed at all. - If a
queryClientsits in the router context, treat the router as a coordinator, not a cache (tanstack-query.md).
Registering The Router
Type safety across the whole project depends on two things: the generated route tree, and declaration merging.
// src/router.tsx
import { createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
export const router = createRouter({
routeTree,
defaultPreload: 'intent',
})
declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}Without the Register block, every bare import from the library falls back to
loose types and autocomplete for to, params, and search disappears.
Anatomy Of A Route
Every non-root route is created with createFileRoute, whose single string
argument is written and maintained by the generator. Do not hand-edit it;
move or rename the file and let the plugin update it.
// src/routes/posts/$postId.tsx
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/posts/$postId')({
loader: ({ params }) => fetchPost(params.postId),
component: PostComponent,
})
function PostComponent() {
const { postId } = Route.useParams()
const post = Route.useLoaderData()
return <h1>{post.title}</h1>
}The root route is different: createRootRoute(), or
createRootRouteWithContext<T>()() when the router carries context. It has no
path, is always matched, and its component always renders. It does not
support code splitting.
The Route object exposes typed hooks — useParams, useSearch,
useLoaderData, useLoaderDeps, useMatch, useRouteContext. Use them instead
of the bare exports whenever you are inside the route's own component.
File Naming, In One Table
Full detail in file-routing.md; this is the working subset.
| Token | Meaning |
__root.tsx | Root route file, at the top of routesDirectory. |
. | Nesting separator — posts.$postId.tsx nests under posts.tsx. |
$label | Dynamic segment, captured into params.label. |
$ alone | Splat route; the rest of the pathname lands in params._splat. |
{-$label} | Optional segment — matches with or without it. |
_ prefix | Pathless layout route: wraps children without adding a path. |
_ suffix | Non-nested route: escapes its parent's component tree. |
- prefix | Excluded from the route tree; use it to colocate components. |
(folder) | Route group — organizational only, contributes no path segment. |
index | Matches the parent path exactly. |
route.tsx | The route file for its containing directory. |
[x] | Escapes a special character — script[.]js.tsx becomes /script.js. |
Directory nesting and . nesting are interchangeable and mixable. Choose per
subtree: directories for wide hierarchies, dots for a few deep ones.
Type-Safe Navigation
Every navigation is relative and has both an origin (from) and a destination
(to). Without from, the router assumes the root and only autocompletes
absolute paths.
<Link to="/posts/$postId" params={{ postId: '123' }} hash="comments">
Post 123
</Link>Rules that prevent most navigation bugs:
- Never interpolate values into
to. Path params go inparams, query state insearch, the fragment inhash.tostays a literal route pattern. paramsandsearchaccept a function of the previous value — use it to preserve existing state instead of rebuilding it.to="."reloads the current (orfrom) location;to=".."goes to the parent. Both are relative tofromwhen supplied.- Prefer
<Link>for anything clickable — it renders a real<a href>, so cmd/ctrl-click and middle-click work. Reach foruseNavigateonly for side-effect navigation, such as after a successful submit. redirect()thrown frombeforeLoadis the right tool for guards; a client-side navigation is not a substitute for a server redirect.- Pass
from={Route.fullPath}rather than a hand-written string, so refactors follow.
Active state is available three ways: activeProps/inactiveProps, the
data-status="active" attribute, and a function child receiving { isActive }.
By default a link is active on pathname prefix match with search params
compared inclusively; pass activeOptions={{ exact: true }} for a home link.
See navigation.md for linkOptions, preloading strategies,
and route masking.
Search Params Are Typed State
TanStack Router parses the query string into JSON, not flat strings, and
validateSearch is the boundary that turns it into a type your app can trust.
The resulting type flows into the route's own options and every child route.
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'
const productSearchSchema = z.object({
page: z.number().catch(1),
filter: z.string().catch(''),
sort: z.enum(['newest', 'oldest', 'price']).catch('newest'),
})
export const Route = createFileRoute('/shop/products')({
validateSearch: productSearchSchema,
component: ProductList,
})
function ProductList() {
const { page, filter, sort } = Route.useSearch()
return <div>...</div>
}Key points:
validateSearchaccepts a function or any object with aparsemethod, so a schema can be passed directly.- Malformed params come from user-editable text. Prefer a fallback (
.catch()) over a hard failure unless an error screen is genuinely the right UX — a throw triggersonErrorwitherror.routerCodeset toVALIDATE_SEARCHand renders theerrorComponent. - With Zod v3 and
.default(),searchbecomes required on everyLinkto that route. Use@tanstack/zod-adapter'szodValidator(plus itsfallbackhelper) to keep input and output types distinct. Zod v4, Valibot, ArkType, and Effect/Schema implement Standard Schema and need no adapter. - Write search params with
<Link search={prev => ...}>ornavigate({ search }). From a component shared across routes, useto="."orstrict: falseto opt into looser types deliberately.
Loaders, Context, Dependencies
A loader runs before the route renders, in parallel with sibling loaders, and
its result is cached under the route's parsed pathname plus whatever
loaderDeps returns.
export const Route = createFileRoute('/posts')({
validateSearch: z.object({ offset: z.number().catch(0) }),
loaderDeps: ({ search: { offset } }) => ({ offset }),
loader: ({ deps: { offset }, context, abortController }) =>
context.fetchPosts({ offset, signal: abortController.signal }),
})- Search params are deliberately absent from
loaderarguments. Anything the loader reads from search must pass throughloaderDeps, otherwise the cache and preloading key on the wrong identity. - Return only the deps you actually use.
loaderDeps: ({ search }) => searchreloads the route when any unrelated param changes. loaderDepsandparams.parserun during route planning, possibly more than once. Keep them deterministic and side-effect-free.beforeLoadruns before the loader and before all childbeforeLoads. Its return value merges into the context for the route and its descendants. Throwredirect()here for guards, and re-throw redirects caught in a try/catch —isRedirect(error)tells them apart from real failures.- Context is the dependency-injection channel: type it with
createRootRouteWithContext<T>(), seed it increateRouter({ context }), and extend it per route viabeforeLoad.
Defaults worth knowing: staleTime is 0 (stale immediately, revalidated in the
background), preloaded data is fresh for 30 s, garbage collection windows are
5 min, the pending component appears after 1 s and then stays for at
least 500 ms. Full options in data-loading.md.
Errors, Pending, Not Found
| Situation | Route option | Notes |
| Loader threw | errorComponent | Receives error and reset. |
| Loader is slow | pendingComponent | Gated by pendingMs / pendingMinMs. |
| Resource missing | notFoundComponent | Throw notFound() from the loader. |
| Side-effect on failure | onError / onCatch | Logging, reporting. |
- After a load failure, prefer
router.invalidate()overreset(): it reruns the loader and resets the boundary together. - Throw
notFound()in the loader, not in the component — that keeps loader data correctly typed and avoids a flicker. - Inside
notFoundComponent,useLoaderDatamay be undefined;useParams,useSearch, anduseRouteContextremain safe. - Only routes that render an
<Outlet />can host a not-found boundary, so give the root route anotFoundComponentor the router adefaultNotFoundComponent.
Code Splitting
The router splits critical config (path parsing, validateSearch, beforeLoad,
loader, context) from non-critical config (component, errorComponent,
pendingComponent, notFoundComponent). The loader is intentionally not
split: it is already an async boundary and it is the most valuable thing to
preload.
Prefer autoCodeSplitting: true in the bundler plugin. It requires file-based
routing with a supported bundler and does not work with the CLI alone. Otherwise
move the non-critical exports into a <route>.lazy.tsx file using
createLazyFileRoute, which accepts only those four component options.
When a component lives outside its route file, reach for getRouteApi('/path')
instead of importing the Route object — same typed hooks, no circular import.
Review Checklist
- The router is registered via
declare moduledeclaration merging, androuteTree.gen.tsis generated, not hand-edited. - Route file names express the intended tree:
_for pathless layouts,(...)for grouping,-for colocated non-route files,indexfor exact matches. - No value is interpolated into
to; params, search, and hash use their own props, andfrom={Route.fullPath}is used for relative navigation. - Every route reading query state defines
validateSearch, with fallbacks for malformed input and an adapter where the validation library needs one. loaderDepsreturns exactly the search values the loader consumes — no more.beforeLoadguards throwredirect()and re-throw caught redirects viaisRedirect; the guard is not treated as an authorization boundary for data.staleTime,preloadStaleTime, andgcTimeare set deliberately per route where the defaults do not fit;defaultPreloadStaleTime: 0when an external cache owns freshness.- Error, pending, and not-found components exist at the levels that render an
<Outlet />; loader-failure retries callrouter.invalidate(). - Components outside their route file use
getRouteApi, not aRouteimport. - Shared components either narrow with
from/toor opt intostrict: falseexplicitly — never rely on the accidental union. - Full-stack concerns (server routes, server functions, SSR setup) live in the
tanstack-startskill, not here.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

