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.

Code-based routing gives you full control over your route tree by manually defining routes using JavaScript/TypeScript. This approach offers maximum flexibility and explicit control over your application’s routing structure.

Overview

With code-based routing, you create routes using the createRoute API and compose them into a route tree. Benefits include:
  • Explicit configuration - Full visibility into your route structure
  • Runtime flexibility - Conditionally create routes based on runtime logic
  • No build step - Works without bundler plugins
  • Type safety - Full TypeScript support

Basic setup

1

Create the root route

Every router needs a root route:
src/routes.tsx
import { createRootRoute, Outlet } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'

const rootRoute = createRootRoute({
  component: () => (
    <>
      <div>
        <nav>
          <Link to="/">Home</Link>
          <Link to="/about">About</Link>
        </nav>
      </div>
      <hr />
      <Outlet />
      <TanStackRouterDevtools />
    </>
  ),
})
2

Create child routes

Define individual routes with createRoute:
src/routes.tsx
const indexRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/',
  component: () => <div>Welcome Home!</div>,
})

const aboutRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/about',
  component: () => <div>About Us</div>,
})
3

Build the route tree

Combine routes into a tree structure:
src/routes.tsx
const routeTree = rootRoute.addChildren([
  indexRoute,
  aboutRoute,
])
4

Create the router

Initialize the router with your route tree:
src/main.tsx
import { createRouter, RouterProvider } from '@tanstack/react-router'
import { routeTree } from './routes'

const router = createRouter({ routeTree })

// Register for type safety
declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router
  }
}

function App() {
  return <RouterProvider router={router} />
}

Route configuration

Data loading

Define loaders to fetch data before rendering:
const postRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/posts/$postId',
  loader: async ({ params }) => {
    const post = await fetchPost(params.postId)
    return post
  },
  component: PostComponent,
})

function PostComponent() {
  const post = postRoute.useLoaderData()
  return <h1>{post.title}</h1>
}

Search params validation

Validate and type search parameters:
import { z } from 'zod'

const postsRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/posts',
  validateSearch: z.object({
    page: z.number().default(1),
    filter: z.string().optional(),
  }),
})

function PostsList() {
  const { page, filter } = postsRoute.useSearch()
  // page and filter are fully typed!
}

Nested routes

Create layout routes with children:
const postsLayoutRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/posts',
  component: () => (
    <div>
      <h1>Posts</h1>
      <Outlet />
    </div>
  ),
})

const postsIndexRoute = createRoute({
  getParentRoute: () => postsLayoutRoute,
  path: '/',
  component: () => <div>Select a post</div>,
})

const postDetailRoute = createRoute({
  getParentRoute: () => postsLayoutRoute,
  path: '/$postId',
  component: PostDetail,
})

const routeTree = rootRoute.addChildren([
  postsLayoutRoute.addChildren([
    postsIndexRoute,
    postDetailRoute,
  ]),
])

Lazy loading

Split routes for code splitting:
const postsRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/posts',
}).lazy(() => import('./routes/posts.lazy').then((m) => m.Route))
src/routes/posts.lazy.tsx
import { createLazyRoute } from '@tanstack/react-router'

export const Route = createLazyRoute('/posts')({
  component: PostsComponent,
})

function PostsComponent() {
  return <div>Posts</div>
}

Error handling

Define error boundaries:
const postRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/posts/$postId',
  loader: async ({ params }) => {
    const post = await fetchPost(params.postId)
    if (!post) throw new Error('Post not found')
    return post
  },
  errorComponent: ({ error }) => (
    <div className="error">
      <h2>Error loading post</h2>
      <p>{error.message}</p>
    </div>
  ),
  component: PostComponent,
})

Path parameters

Define dynamic segments:
// Single parameter
const userRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/users/$userId',
})

// Multiple parameters
const fileRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/files/$projectId/$fileId',
})

// Catch-all parameter
const docsRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/docs/$',
})

Type registration

Register your router for global type safety:
const router = createRouter({ routeTree })

declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router
  }
}
Type registration enables autocomplete and type checking throughout your application.

Best practices

Group related routes together to keep your route tree maintainable.
Split heavy components to reduce initial bundle size.
Always validate search params for type safety and runtime correctness.

Next steps

File-based routing

Explore automatic route generation

Data loading

Learn advanced data loading patterns