DEV Community

Alex Spinov
Alex Spinov

Posted on

TanStack Router Has a Free API You Should Know About

TanStack Router brings 100% type-safe routing to React. Its API for search params, loaders, and nested layouts is far more powerful than React Router.

Type-Safe Route Definitions

import { createFileRoute } from "@tanstack/react-router"
import { z } from "zod"

export const Route = createFileRoute("/products")({ 
  validateSearch: z.object({
    page: z.number().default(1),
    sort: z.enum(["name", "price", "date"]).default("date"),
    q: z.string().optional()
  }),

  loaderDeps: ({ search }) => ({ page: search.page, sort: search.sort }),

  loader: async ({ deps }) => {
    return fetchProducts({ page: deps.page, sort: deps.sort })
  },

  component: ProductsPage
})

function ProductsPage() {
  const products = Route.useLoaderData()
  const search = Route.useSearch()
  const navigate = Route.useNavigate()

  return (
    <div>
      <select
        value={search.sort}
        onChange={(e) => navigate({ search: { sort: e.target.value } })}
      >
        <option value="name">Name</option>
        <option value="price">Price</option>
        <option value="date">Date</option>
      </select>
      {products.map(p => <ProductCard key={p.id} product={p} />)}
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Type-Safe Links

import { Link } from "@tanstack/react-router"

// TypeScript knows all valid routes and their params!
<Link to="/products" search={{ page: 1, sort: "price" }}>
  Products
</Link>

<Link to="/products/$productId" params={{ productId: "123" }}>
  Product Detail
</Link>

// Type error! "invalid" is not a valid sort option
<Link to="/products" search={{ sort: "invalid" }}>
  Oops
</Link>
Enter fullscreen mode Exit fullscreen mode

Route Context & Auth

// routes/__root.tsx
export const Route = createRootRouteWithContext<{
  auth: AuthContext
  queryClient: QueryClient
}>()({
  component: RootComponent
})

// routes/_authenticated.tsx
export const Route = createFileRoute("/_authenticated")({
  beforeLoad: ({ context }) => {
    if (!context.auth.isAuthenticated) {
      throw redirect({ to: "/login" })
    }
  }
})

// routes/_authenticated/dashboard.tsx
export const Route = createFileRoute("/_authenticated/dashboard")({
  loader: async ({ context }) => {
    return context.queryClient.ensureQueryData(dashboardQuery())
  },
  component: Dashboard
})
Enter fullscreen mode Exit fullscreen mode

Pending UI & Streaming

export const Route = createFileRoute("/slow-page")({
  loader: () => fetchSlowData(),
  pendingComponent: () => <FullPageSpinner />,
  pendingMs: 200, // Show pending after 200ms
  pendingMinMs: 500 // Show for at least 500ms
})
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

  • 100% type-safe routes, params, search params, loaders
  • File-based routing with code generation
  • Search param validation with Zod
  • Route context for auth and dependency injection
  • Pending UI with configurable timings
  • Preloading on hover for instant navigation

Explore TanStack Router docs for the complete API.


Building web scrapers or data pipelines? Check out my Apify actors for ready-made solutions, or email spinov001@gmail.com for custom development.

Top comments (0)