react-tanstack-router-query — правило для Cursor: суть и установка
Настраивает связку TanStack Router v1 и TanStack Query v5 в React-проектах для загрузки данных в роутере без спиннеров ожидания.
.cursor/rules/react-tanstack-router-query.mdcЧто делает react-tanstack-router-query
Правило инструктирует ассистента генерировать архитектуру React SPA на Vite и TypeScript с упором на кэш. Запросы данных выносятся в loader маршрута через queryClient.ensureQueryData, поэтому UI монтируется уже с готовыми данными из кэша. Роутер отвечает за навигацию и состояние URL, а TanStack Query управляет серверным состоянием и мутациями.
Шаблон требует объявлять фабрики queryOptions строго вне компонентов и полностью запрещает фетчинг внутри useEffect. Фильтры и пагинация синхронизируются с параметрами адресной строки через валидацию схем Zod. При мутациях кэш прогревается вручную вызовом setQueryData, после чего вызываются invalidateQueries и мгновенный переход на нужный адрес.
Инструкция применяется только к файлам в src/routes/**/*, src/queries/**/*, src/lib/router.ts и src/lib/queryClient.ts. Для работы строгой типизации необходима декларация модуля через declare module '@tanstack/react-router', иначе TypeScript не сможет проверять пути и параметры роутера.
Какие задачи решает
- Связать TanStack Router v1 и TanStack Query v5 без лишних экранов загрузки
- Вынести запросы данных из компонентов в лоадеры маршрутов
- Синхронизировать параметры URL с ключами кэша запросов
- Прогревать кэш после мутаций до выполнения перехода
Примеры запросов агенту
- Создай маршрут
src/routes/posts/$postId.tsxс загрузчиком черезpostQueryOptionsи выводом статьи. - Добавь список записей с пагинацией через параметры URL и валидацией схемы поиска.
- Напиши мутацию создания поста, которая обновляет кэш через
setQueryDataи перенаправляет на страницу записи.
Как установить react-tanstack-router-query
Вручную: положить файлы
.cursor/rules/react-tanstack-router-query.mdcВо frontmatter: description, globs, alwaysApply
Вручную: положить файлы
.claude/rules/react-tanstack-router-query.mdПоле paths во frontmatter ограничивает файлы: globs из источника → paths
AGENTS.md
Codex читает AGENTS.md: вставьте текст правила туда
Вручную: положить файлы
.github/instructions/react-tanstack-router-query.instructions.mdПоле applyTo задаёт, к каким файлам применять: globs из источника → applyTo
Вручную: положить файлы
.devin/rules/react-tanstack-router-query.mdВручную: положить файлы
.clinerules/react-tanstack-router-query.mdПо шагам, с командами для каждой платформы: Cursor rules: файлы .mdc, четыре режима и замена .cursorrules
Вопросы о react-tanstack-router-query
Как установить react-tanstack-router-query в Cursor?
Создайте файл .cursor/rules/react-tanstack-router-query.mdc в корне проекта и скопируйте в него содержимое правила.
Для каких файлов действует это правило?
Оно срабатывает при редактировании src/routes/**/*, src/queries/**/*, src/lib/router.ts и src/lib/queryClient.ts.
Почему правило запрещает получать данные через useEffect?
Данные запрашиваются в loader до рендеринга страницы с помощью ensureQueryData, поэтому компонент сразу берёт готовый ответ из кэша.
Исходный текст
react-tanstack-router-query.mdc · CC0-1.0
---
description: "React SPA with TanStack Router v1 + TanStack Query v5 — the definitive pattern for zero-loading-spinner routing, type-safe URLs, and cache-first data"
globs: ["src/routes/**/*", "src/queries/**/*", "src/lib/router.ts", "src/lib/queryClient.ts"]
alwaysApply: false
---
You are an expert in React, TanStack Router v1, TanStack Query v5, TypeScript, and Vite.
## Architecture
- TanStack Router: routing, URL state, navigation
- TanStack Query: server state, caching, mutations
- Loader = bridge: prefetches into Query cache before render → zero loading spinners for route data
- Components are pure UI: read from Query cache, trigger mutations
## Setup
```ts
// src/lib/queryClient.ts
export const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 60_000 } },
})
// src/lib/router.ts
export const router = createRouter({
routeTree,
context: { queryClient },
defaultPreload: 'intent',
defaultPreloadStaleTime: 0,
})
declare module '@tanstack/react-router' {
interface Register { router: typeof router }
}
// src/main.tsx
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} context={{ queryClient }} />
</QueryClientProvider>
```
## Query Definitions
```ts
// src/queries/posts.ts
export const postKeys = {
all: ['posts'] as const,
detail: (id: string) => [...postKeys.all, 'detail', id] as const,
list: (f?: PostFilters) => [...postKeys.all, 'list', f] as const,
}
export const postQueryOptions = (id: string) =>
queryOptions({ queryKey: postKeys.detail(id), queryFn: () => fetchPost(id) })
export const postsQueryOptions = (filters?: PostFilters) =>
queryOptions({ queryKey: postKeys.list(filters), queryFn: () => fetchPosts(filters) })
```
## Loader + Component (zero loading state)
```tsx
export const Route = createFileRoute('/posts/$postId')({
loader: ({ context: { queryClient }, params }) =>
queryClient.ensureQueryData(postQueryOptions(params.postId)),
component: PostDetail,
})
function PostDetail() {
const { postId } = Route.useParams()
const { data: post } = useQuery(postQueryOptions(postId)) // always in cache from loader
return <h1>{post!.title}</h1>
}
```
## Search Params → Query Key
```tsx
const searchSchema = z.object({ page: z.number().default(1), q: z.string().optional() })
export const Route = createFileRoute('/posts/')({
validateSearch: searchSchema,
loader: ({ context: { queryClient }, location: { search } }) =>
queryClient.ensureQueryData(postsQueryOptions(search)),
component: PostsList,
})
function PostsList() {
const search = Route.useSearch()
const { data } = useQuery(postsQueryOptions(search))
// ...
}
```
## Mutations
```tsx
const mutation = useMutation({
mutationFn: createPost,
onSuccess: (newPost) => {
queryClient.setQueryData(postKeys.detail(newPost.id), newPost) // warm cache
queryClient.invalidateQueries({ queryKey: postKeys.list() })
navigate({ to: '/posts/$postId', params: { postId: newPost.id } }) // instant — no spinner
},
})
```
## Hover Prefetching
```tsx
<Link
to="/posts/$postId"
params={{ postId: post.id }}
onMouseEnter={() => queryClient.prefetchQuery(postQueryOptions(post.id))}
>
{post.title}
</Link>
```
## Key Rules
- Always define `queryOptions` outside components — never inline inside `useQuery()`
- Never use `useEffect` for data fetching — use loaders or `useQuery`
- Search params are the single source of truth for filter/pagination state
- After mutations: `setQueryData` + `invalidateQueries` for instant UI feedback
- `declare module '@tanstack/react-router'` router registration is required for full type safety
© PatrickJS · CC0-1.0 · PatrickJS/awesome-cursorrules