There are tons of web frameworks out there. For React alone, we have React Router, Next.js, Remix, TanStack Router, and more. Now, most of them are supporting full-stack features like SSR. Outside of the React world, we have Nuxt, Svelte, Solid, and others. But still, TanStack Router is the best router of all web frameworks!
In most web apps, most workloads are CRUD operations to handle different data flows from the server. Take a todo list app as an example: we have a page for the todo items table and a page for individual todo item details. We need to fetch data from the server for both of them.
/todos
/todos/:idBut for the list page, what happens when we want to add more features like filtering, sorting, and pagination? How would you store those states? You can either put them into component state or use them as parameters in the URL.
Usually, putting them in the URL is more user-friendly since users can share URLs with others and bookmark them. However, from a code perspective, URLs are just strings, making it inherently harder to manage complex states while maintaining type safety. Of course, with Next.js we have nuqs. But for TanStack Router, we have a better, more type-safe, native solution.
useSearch: A Type-Safe SolutionTanStack Router treats search params as structured JSON data rather than simple string key-value pairs. It automatically converts URL search strings to typed objects and back, providing type safety through validation schemas.
Here's what makes it powerful:
// This navigation...
<Link
to="/shop"
search={{
pageIndex: 3,
includeCategories: ["electronics", "gifts"],
sortBy: "price",
desc: true,
}}
>
Shop
</Link>
// Creates this URL:
// /shop?pageIndex=3&includeCategories=%5B%22electronics%22%2C%22gifts%22%5D&sortBy=price&desc=true
// Which parses back to:
{
"pageIndex": 3,
"includeCategories": ["electronics", "gifts"],
"sortBy": "price",
"desc": true
}Notice how arrays and complex data structures are automatically serialized and deserialized? That's the magic!
The real power comes from validation schemas. Here's an example using Zod:
// routes/shop.products.tsx
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'
import { zodValidator } from '@tanstack/zod-adapter'
const productSearchSchema = z.object({
page: z.number().default(1),
filter: z.string().default(''),
sort: z.enum(['newest', 'oldest', 'price']).default('newest'),
})
export const Route = createFileRoute('/shop/products')({
validateSearch: zodValidator(productSearchSchema),
component: ProductList,
})
function ProductList() {
const { page, filter, sort } = Route.useSearch()
return (
<div>
<p>Page: {page}</p>
<p>Filter: {filter}</p>
<p>Sort: {sort}</p>
</div>
)
}With Route.useSearch(), you get fully typed search params with autocomplete! No more string parsing or type assertions needed.
Updating search params is equally elegant. You can use the Link component with a function to update specific params:
function ProductList() {
return (
<div>
{/* Update specific params */}
<Link
from="/shop/products"
search={(prev) => ({ ...prev, page: prev.page + 1 })}
>
Next Page
</Link>
{/* Set new search params */}
<Link
to="/shop/products"
search={{ page: 1, filter: 'electronics', sort: 'price' }}
>
Electronics
</Link>
</div>
)
}Or programmatically with useNavigate:
import { useNavigate } from '@tanstack/react-router'
function ProductList() {
const navigate = useNavigate({ from: '/shop/products' })
const handleNextPage = () => {
navigate({
search: (prev) => ({ ...prev, page: prev.page + 1 }),
})
}
return <button onClick={handleNextPage}>Next Page</button>
}Beyond search params, Tanstack Router offers other powerful features as well such as the integration with Tanstack Query, and more. Still, the seamless handling of search params is what impresses me most. Whenever I explore a new framework, I always check if it provides features similar to Tanstack Router.
If you haven’t tried Tanstack Router yet, I highly recommend giving it a go — you’re truly missing out! I’ve also compiled some notes from my own learning journey, which you can find here. Feel free to check them out if you’d like to explore further!