---
id: react-tanstack-router-patterns
title: TanStack Router — Type-safe / loader / search params
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [react, router, tanstack-router, vibe-coding]
tech_stack: { language: "TS / React", applicable_to: ["Frontend"] }
applied_in: []
aliases: [TanStack Router, file-based routing, type-safe params, loader, search validation]
---

# TanStack Router

> Type-safe React router. **Search params validation, file-based routing, loader, suspense** 일급. React Router 의 type-safe 버전 + Next 의 file-based.

## 📖 핵심 개념
- File-based: `routes/*.tsx` → 자동 tree.
- Type-safe link: `<Link to="/users/$id" params={{ id: '1' }} />` 컴파일 검증.
- Search params: zod 같은 schema.
- Loader: 라우트 진입 전 데이터 fetch.

## 💻 코드 패턴

### Setup
```bash
yarn add @tanstack/react-router @tanstack/router-plugin
```

```ts
// vite.config.ts
import { TanStackRouterVite } from '@tanstack/router-plugin/vite';
plugins: [TanStackRouterVite()];
```

### Route 정의 (file-based)
```tsx
// src/routes/__root.tsx
import { Outlet, createRootRoute } from '@tanstack/react-router';
export const Route = createRootRoute({ component: () => <Outlet /> });

// src/routes/users.$id.tsx
import { createFileRoute } from '@tanstack/react-router';
import { z } from 'zod';

export const Route = createFileRoute('/users/$id')({
  params: { parse: (p) => ({ id: z.string().uuid().parse(p.id) }) },
  validateSearch: z.object({ tab: z.enum(['profile', 'orders']).default('profile') }),
  loader: async ({ params }) => api.user.get(params.id),
  component: UserPage,
});

function UserPage() {
  const { id } = Route.useParams();          // typed
  const { tab } = Route.useSearch();         // typed
  const user = Route.useLoaderData();         // typed
  return <h1>{user.name}</h1>;
}
```

### Link (type-safe)
```tsx
import { Link } from '@tanstack/react-router';

<Link to="/users/$id" params={{ id: '42' }} search={{ tab: 'orders' }}>
  Profile
</Link>

// 잘못된 path / params 컴파일 에러
```

### Navigate (programmatic)
```tsx
const navigate = Route.useNavigate();
navigate({ to: '/users/$id', params: { id: '42' } });

// 또는 search 만 update
navigate({ search: (prev) => ({ ...prev, tab: 'orders' }) });
```

### Loader + Suspense
```tsx
export const Route = createFileRoute('/posts')({
  loader: async () => api.posts.list(),
  pendingComponent: () => <Spinner />,
  errorComponent: ({ error }) => <Error error={error} />,
  component: PostsPage,
});

function PostsPage() {
  const posts = Route.useLoaderData();
  return ...
}
```

### TanStack Query 통합 (loader 안)
```ts
import { queryOptions } from '@tanstack/react-query';

const userQuery = (id: string) => queryOptions({
  queryKey: ['user', id],
  queryFn: () => api.user.get(id),
});

export const Route = createFileRoute('/users/$id')({
  loader: ({ context, params }) => context.queryClient.ensureQueryData(userQuery(params.id)),
  component: UserPage,
});

function UserPage() {
  const { id } = Route.useParams();
  const { data } = useSuspenseQuery(userQuery(id));
  return <h1>{data.name}</h1>;
}
```

### Search params (typed link)
```tsx
<Link to="/posts" search={{ q: 'react', page: 2 }}>Posts</Link>

const { q, page } = Route.useSearch();
```

URL: `/posts?q=react&page=2`.

### Nested layout
```tsx
// routes/_app.tsx — layout
export const Route = createFileRoute('/_app')({
  component: () => (
    <div className="app">
      <Sidebar />
      <Outlet />
    </div>
  ),
});

// routes/_app.users.tsx — _app layout 안
export const Route = createFileRoute('/_app/users')({...});
```

### Auth (beforeLoad)
```ts
export const Route = createFileRoute('/admin')({
  beforeLoad: async ({ context }) => {
    if (!context.auth.user) throw redirect({ to: '/login' });
  },
});
```

### Devtools
```tsx
import { TanStackRouterDevtools } from '@tanstack/router-devtools';

<RouterProvider router={router} />
<TanStackRouterDevtools router={router} />
```

## 🤔 의사결정 기준
| 상황 | 추천 |
|---|---|
| Type-safe critical | TanStack Router |
| Next.js | Next App Router (자체) |
| 단순 SPA | React Router 6 |
| 거대 monorepo + 강 types | TanStack Router |
| File-based + SSR | Next / Remix |
| 빠른 prototype | React Router |

## ❌ 안티패턴
- **String path concat**: type 안전 X. params object.
- **Search 검증 없음**: 잘못된 URL 이 crash.
- **Loader 안에 mutation**: 의도와 다름. action 으로.
- **Route 별 `useParams<RouteId>()` 매번 type 인자**: factory 활용.
- **beforeLoad 무거운 작업**: 라우트 진입 느림.
- **404 / pendingComponent 없음**: 빈 화면.

## 🤖 LLM 활용 힌트
- File-based + zod search/params + loader + TQ 통합 4종.
- Vite plugin 자동 generate.
- Devtools 필수 (개발).

## 🔗 관련 문서
- [[React_TanStack_Query_Advanced]]
- [[React_Router_Patterns]]
- [[React_Suspense_for_Data]]
