---
id: url-state
track: learn
product: router
locale: en
order: 4
revision: 2
sourceCheckedOn: "2026-08-03"
versionRange: "Router ^1"
verifiedAgainst: "@tanstack/react-router@1.170.18"
contentModel: 2
prerequisites:
  - "File route basics"
sourceRefs:
  - "https://tanstack.com/router/latest/docs/guide/search-params"
---

# Put filters in the URL

> Build shareable, refresh-safe, typed issue filters with validated search params.

**Outcome:** Decide what belongs in the URL and give it stable defaults.

## Search params are external input

Users can edit URLs and old bookmarks can contain stale data. Validate at the route boundary instead of scattering Number() calls through components.

File: `src/routes/issues/index.tsx`

```tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/issues/')({
  validateSearch: (search: Record<string, unknown>) => ({
    page: Math.max(1, Number(search.page) || 1),
    status: search.status === 'closed' ? 'closed' : 'open',
  }),
  component: IssuesPage,
})

function IssuesPage() {
  const filters = Route.useSearch()
  return <p>Page {filters.page} · {filters.status}</p>
}
```

## What belongs in the URL

Filters, sorting, pagination, and selected tabs usually belong in the URL because users share, bookmark, navigate, and refresh them. Transient details like hover state do not.

## Practice and verification

### Completion checkpoint

Filters, status, and page restore from the URL, so refreshes and shared links preserve the view.

### Exercise

Add a priority filter with a default and omit that default when writing the URL.

### Verification

An invalid page value should fall back safely, while a valid copied URL should reproduce the same filtered result.

### Common pitfalls

- Using a TypeScript assertion without runtime validation.
- Accidentally clearing unrelated search parameters while updating one filter.

## Official sources

- https://tanstack.com/router/latest/docs/guide/search-params
