How to Integrate TanStack Router in React

January, 16th 2025 4 min read

TanStack Router is a modern, type-safe, and performance‑oriented routing solution for React applications. It is built on the same design philosophy as other TanStack libraries: predictable APIs, first‑class TypeScript support, and powerful tooling for data‑driven user interfaces. This article provides a full technical overview of TanStack Router, covering setup, routing models, search‑parameter validation, loaders, SSR, devtools, and advanced features such as structural sharing and route masking.

Why TanStack Router Exists

Traditional routers such as React Router offer flexibility but lack strict type validation and built‑in data mechanisms. Framework routers such as Next.js limit flexibility because routing is tightly coupled to the framework. TanStack Router takes the middle ground: a completely framework‑agnostic, type‑first router for React.

Key goals:

  • Provide strong type guarantees at compile time
  • Integrate routing with data loading and caching
  • Enable declarative search‑parameter management
  • Support code splitting, SSR, file‑based routing
  • Give developers fine‑grained control without requiring a full framework

Core Features

Type‑Safe Routing

Every route, its path, loader, parameters, and search schema are fully typed. When navigating, incorrect keys, invalid parameters, or missing search fields are caught at compile time.

Built‑In Data Loaders

Routes can define loader() functions that:

  • fetch data before rendering
  • cache results
  • revalidate automatically
  • integrate with TanStack Query for complex workflows

Search Parameter Validation

Unlike manually parsing URLSearchParams, TanStack Router allows:

  • runtime validation
  • default values
  • type‑safe updates
  • JSON‑compatible search objects

SSR and Streaming

TanStack Router supports:

  • non‑streaming SSR: load data → render → hydrate
  • streaming SSR: progressively deliver HTML while loaders resolve

Both patterns integrate naturally with existing React SSR methods.

Code Splitting and File‑Based Routing

  • routes can be defined in code or generated from files
  • lazy imports work seamlessly
  • subtree routing supports extremely flexible directory structures

Devtools

A dedicated panel visualizes:

  • route tree
  • loader states
  • cache snapshots
  • search parameters
  • navigation events

Getting Started

Code‑Based Routing

ts
import {
  createRouter,
  createRootRoute,
  createRoute,
  RouterProvider,
} from '@tanstack/react-router';

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

const router = createRouter({
  routeTree: rootRoute.addChildren([aboutRoute]),
});

// Application
<RouterProvider router={router} />;
ts
import {
  createRouter,
  createRootRoute,
  createRoute,
  RouterProvider,
  Outlet,
  Link,
} from '@tanstack/react-router';
import { StrictMode } from 'react';
import ReactDOM from 'react-dom/client';
import { TanStackRouterDevtools } from '@tanstack/router-devtools';

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

const indexRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/',
  component: () => <div>Home Page</div>,
});

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

const routeTree = rootRoute.addChildren([indexRoute, aboutRoute]);
const router = createRouter({ routeTree });

declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router;
  }
}

ReactDOM.createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <RouterProvider router={router} />
  </StrictMode>,
);

File‑Based Routing

When using file‑based routing, /src/routes becomes the source of truth:

plaintext
src/
 ┣ routes/
 ┃  ┣ __root.tsx
 ┃  ┣ index.tsx
 ┃  ┗ about.tsx
 ┣ main.tsx
 ┗ routeTree.gen.ts

__root.tsx typically contains global layout, headers, and devtools.

Data Loaders

Loaders fetch data before rendering and cache the results.

ts
import { createFileRoute } from '@tanstack/react-router';
import { fetchPosts } from '@/api';

export const Route = createFileRoute('/posts')({
  loader: async () => fetchPosts(),
  component: Posts,
});

function Posts() {
  const posts = Route.useLoaderData();
  return (
    <ul>
      {posts?.map((p) => (
        <li key={p.id}>{p.title}</li>
      ))}
    </ul>
  );
}

Search Parameters and Validation

ts
const ProductsRoute = createFileRoute('/products')({
  validateSearch: (search) => ({
    ...search,
    filter: search.filter ?? '',
  }),
  component: Products,
});

function Products() {
  const { filter } = ProductsRoute.useSearch();
  const navigate = useNavigate({ from: ProductsRoute.fullPath });

  function setActiveFilter() {
    navigate({
      search: (old) => ({ ...old, filter: 'active' }),
    });
  }

  return (
    <div>
      <p>Current Filter: {filter}</p>
      <button onClick={setActiveFilter}>Active Products</button>
    </div>
  );
}

Render Optimizations

Structural Sharing

If only part of the search object changes, TanStack Router preserves references.

ts
const foo = Route.useSearch({ select: ({ foo }) => foo });

Enable globally:

ts
const router = createRouter({
  routeTree,
  defaultStructuralSharing: true,
});

Fine‑Grained Selectors

ts
const result = Route.useSearch({
  select: (search) => ({
    foo: search.foo,
    greeting: `Hi ${search.foo}`,
  }),
  structuralSharing: true,
});

Advanced Features

Virtual File Routes

Programmatically define or merge route directories.

SSR and Streaming

  • Non‑streaming renders full HTML before hydration
  • Streaming sends HTML chunks progressively

Route Masking

Present one URL while routing internally to a different path.

Auth Routes

Use beforeLoad to guard routes.

Data Mutations

Integrate with TanStack Query, SWR, RTK Query, or Relay. After mutation:

ts
router.invalidate();

Scroll Restoration

Automatically preserve scroll position between navigations.

Error Handling

Supports:

  • errorComponent
  • notFoundComponent
  • throwing notFound() inside loaders

External Data Integrations

Loaders work seamlessly with external clients. Pass them through context for prefetching.

Devtools Integration

ts
import { TanStackRouterDevtools } from '@tanstack/router-devtools';

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

State persists via localStorage.

Conclusion

TanStack Router delivers a modern, type‑safe routing model that scales with real‑world React applications. Its integration with loaders, fine‑grained state selection, JSON‑safe search parameters, SSR, streaming, and file‑based routing make it one of the most flexible routing tools available. For teams that value type safety, predictable data flows, and compatibility with strong caching tools like TanStack Query, it offers a compelling alternative to traditional routers and framework‑locked solutions.

Explore the official docs to take advantage of its full capabilities.