Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tanstack/router/llms.txt

Use this file to discover all available pages before exploring further.

Routes are the building blocks of your application’s navigation. Each route defines a path pattern, data loading logic, and components to render.

Creating Routes

Routes are created using the createRoute function:
import { createRoute } from '@tanstack/react-router'
import { rootRoute } from './root'

const aboutRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/about',
  component: AboutComponent,
})

Root Route

Every route tree starts with a root route:
import { createRootRoute } from '@tanstack/react-router'

const rootRoute = createRootRoute({
  component: () => (
    <div>
      <nav>{/* Navigation */}</nav>
      <Outlet /> {/* Child routes render here */}
    </div>
  ),
})

Route Options

Path Configuration

path
string
required
The path pattern to match. Can include parameters:
path: '/posts/$postId'  // Path parameter
path: '/files/$'        // Wildcard
path: '/posts/{-$postId}' // Optional parameter
id
string
Custom route ID instead of using the path. Useful for routes without paths.

Components

component
React.ComponentType
The component to render when this route matches.
component: () => <div>About Page</div>
pendingComponent
React.ComponentType
Component shown while the route is loading data.
pendingComponent: () => <div>Loading...</div>
errorComponent
React.ComponentType<ErrorComponentProps>
Component shown when an error occurs during loading or rendering.
errorComponent: ({ error, reset }) => (
  <div>
    <p>Error: {error.message}</p>
    <button onClick={reset}>Try Again</button>
  </div>
)
notFoundComponent
React.ComponentType
Component shown when a child route is not found.
notFoundComponent: () => <div>Page not found</div>

Search Parameter Validation

Function or validator to parse and validate search parameters.
validateSearch: (search) => ({
  page: Number(search.page) || 1,
  filter: search.filter as string | undefined,
})

Path Parameter Parsing

params.parse
ParseParamsFn
Transform path parameters from strings to typed values.
params: {
  parse: (params) => ({
    postId: Number(params.postId),
  }),
}
params.stringify
StringifyParamsFn
Transform typed parameters back to strings for URL generation.
params: {
  stringify: (params) => ({
    postId: String(params.postId),
  }),
}

Data Loading

Context

Provide data to child routes and loaders:
const userRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/user',
  context: ({ location }) => ({
    userId: getUserIdFromLocation(location),
  }),
})

Before Load

Run async code before the route loads:
const protectedRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/dashboard',
  beforeLoad: async ({ context, location }) => {
    if (!context.auth.isAuthenticated) {
      throw redirect({
        to: '/login',
        search: { redirect: location.href },
      })
    }
  },
})

Loader

Fetch data for the route:
const postRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/posts/$postId',
  loader: async ({ params }) => {
    const post = await fetchPost(params.postId)
    return { post }
  },
})
Access loader data in components:
function PostComponent() {
  const { post } = useLoaderData({ from: '/posts/$postId' })
  return <div>{post.title}</div>
}

Loader Dependencies

Declare additional dependencies for cache keys:
const postsRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/posts',
  loaderDeps: ({ search }) => ({
    page: search.page,
    filter: search.filter,
  }),
  loader: async ({ deps }) => {
    const posts = await fetchPosts({
      page: deps.page,
      filter: deps.filter,
    })
    return { posts }
  },
})

Caching Options

staleTime
number
default:"0"
Time in milliseconds before cached data is considered stale.
staleTime: 5000 // 5 seconds
gcTime
number
default:"1800000"
Time in milliseconds before unused cached data is garbage collected.
gcTime: 30 * 60 * 1000 // 30 minutes
preloadStaleTime
number
default:"30000"
How long preloaded data stays fresh.
preloadGcTime
number
default:"1800000"
How long preloaded data is cached.

Pending State

pendingMs
number
default:"1000"
Delay in milliseconds before showing pending component.
pendingMs: 500 // Show after 500ms
pendingMinMs
number
default:"500"
Minimum time in milliseconds to show pending component once displayed.

Lifecycle Hooks

onEnter

Called when a route match enters the active matches:
const route = createRoute({
  getParentRoute: () => rootRoute,
  path: '/analytics',
  onEnter: (match) => {
    trackPageView(match.pathname)
  },
})

onStay

Called when a route match stays in active matches during navigation:
onStay: (match) => {
  console.log('Route stayed active:', match.routeId)
}

onLeave

Called when a route match leaves the active matches:
onLeave: (match) => {
  cleanupResources(match)
}

Lazy Loading

Split route components into separate bundles:
const postRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/posts/$postId',
}).lazy(() => import('./post.lazy').then(d => d.postLazyRoute))
In post.lazy.tsx:
import { createLazyRoute } from '@tanstack/react-router'

export const postLazyRoute = createLazyRoute('/posts/$postId')({
  component: PostComponent,
})

SSR Configuration

ssr
boolean | 'data-only'
default:"true"
Control server-side rendering behavior:
  • true - Full SSR
  • false - Client-only
  • 'data-only' - SSR data but not component

Meta Tags and Headers

Define meta tags, links, and scripts:
const postRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/posts/$postId',
  head: ({ loaderData }) => ({
    meta: [
      { title: loaderData.post.title },
      { name: 'description', content: loaderData.post.excerpt },
      { property: 'og:image', content: loaderData.post.image },
    ],
    links: [
      { rel: 'canonical', href: `/posts/${loaderData.post.id}` },
    ],
  }),
})

Headers

Set HTTP response headers:
headers: async ({ loaderData }) => ({
  'Cache-Control': 'public, max-age=3600',
  'X-Custom-Header': loaderData.customValue,
})

Error Handling

Error Component

const route = createRoute({
  getParentRoute: () => rootRoute,
  path: '/posts/$postId',
  errorComponent: ({ error, reset }) => (
    <div>
      <h1>Error Loading Post</h1>
      <p>{error.message}</p>
      <button onClick={reset}>Retry</button>
    </div>
  ),
})

onError Hook

onError: (error) => {
  logErrorToService(error)
}

Redirects

Redirect from a route using the redirect helper:
import { redirect } from '@tanstack/react-router'

const oldRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/old-path',
  beforeLoad: () => {
    throw redirect({ to: '/new-path' })
  },
})
Or use the route’s built-in redirect method:
const postRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/posts/$postId',
  beforeLoad: ({ params }) => {
    if (!isValidPostId(params.postId)) {
      throw postRoute.redirect({ to: '/posts' })
    }
  },
})

Next Steps

Loaders

Deep dive into data loading patterns

Type Safety

Configure end-to-end type safety

Navigation

Navigate between routes programmatically

Path Params

Extract and validate path parameters