nextjs-tanstack-query — правило для Cursor
Задаёт стандарты связки Next.js App Router и TanStack Query v5. Направляет Cursor при настройке гидратации кэша, серверных мутаций и оптимистичных обновлений интерфейса.
.cursor/rules/nextjs-tanstack-query.mdcЧто делает nextjs-tanstack-query
Правило задаёт стандарты для Next.js App Router и TanStack Query v5. Серверные компоненты получают данные напрямую без вызова хуков. Начальное состояние передаётся на клиент через prefetchQuery и обёртку HydrationBoundary. Для каждого серверного запроса создаётся свой QueryClient. Повторное использование инстанса на сервере приведёт к утечке данных между пользователями.
В клиентских компонентах запрещены прямые вызовы fetch. Запросы оформляются через фабрики queryOptions. Мутации вызывают Server Actions внутри mutationFn и сбрасывают кэш через invalidateQueries. Для оптимистичного интерфейса описан откат состояния через onError.
Правило отслеживает пути app/**/*.tsx, app/**/*.ts, src/app/**/*.tsx и src/queries/**/*. Все компоненты с хуками библиотеки помечаются директивой 'use client'.
Какие задачи решает
- настроить гидратацию серверного кэша TanStack Query в компонентах
- организовать мутации данных через Server Actions
- внедрить оптимистичные обновления интерфейса с откатом ошибок
- создать фабрики запросов через queryOptions
Примеры запросов агенту
- Сделай серверный компонент в
app/posts/page.tsxс предзагрузкой постов черезprefetchQueryиHydrationBoundary - Напиши мутацию для
createPostчерез Server Action с инвалидацией кэша списка - Добавь оптимистичное обновление для
updatePostс сохранением предыдущего состояния на случай ошибки
Как установить nextjs-tanstack-query
Вручную: положить файлы
.cursor/rules/nextjs-tanstack-query.mdcВо frontmatter: description, globs, alwaysApply
Вручную: положить файлы
.claude/rules/nextjs-tanstack-query.mdПоле paths во frontmatter ограничивает файлы: globs из источника → paths
AGENTS.md
Codex читает AGENTS.md: вставьте текст правила туда
Вручную: положить файлы
.github/instructions/nextjs-tanstack-query.instructions.mdПоле applyTo задаёт, к каким файлам применять: globs из источника → applyTo
Вручную: положить файлы
.devin/rules/nextjs-tanstack-query.mdВручную: положить файлы
.clinerules/nextjs-tanstack-query.mdПо шагам, с командами для каждой платформы: Cursor rules: файлы .mdc, четыре режима и замена .cursorrules
Вопросы о nextjs-tanstack-query
Как установить nextjs-tanstack-query в Cursor?
Сохраните файл правила по пути .cursor/rules/nextjs-tanstack-query.mdc в корне проекта. Редактор автоматически применит указанные инструкции к подходящим файлам.
Что делает правило nextjs-tanstack-query?
Оно обучает Cursor архитектурным шаблонам интеграции TanStack Query v5 в Next.js App Router. Правило предотвращает утечки данных и подсказывает корректный синтаксис предзагрузки.
Почему нельзя переиспользовать QueryClient на сервере?
Один общий экземпляр сохраняет данные предыдущих запросов и отдаёт их другим пользователям. Правило требует создавать новый QueryClient на каждый серверный запрос.
Исходный текст
nextjs-tanstack-query.mdc · CC0-1.0
---
description: "Next.js App Router combined with TanStack Query v5 — HydrationBoundary pattern, Server Actions as mutations, optimistic updates, and infinite scroll"
globs: ["app/**/*.tsx", "app/**/*.ts", "src/app/**/*.tsx", "src/queries/**/*"]
alwaysApply: false
---
You are an expert in Next.js App Router, TanStack Query v5, TypeScript, and combining server components with client-side data fetching.
## Architecture
- Server Components fetch data directly — no TanStack Query needed there
- TanStack Query lives in Client Components for interactive, real-time, or mutation-driven data
- Hydrate the Query cache from server to avoid client waterfalls on first load
- Use React Server Components for initial page data; TanStack Query for mutations + polling + optimistic UI
## Provider Setup
```tsx
// providers/query-provider.tsx
'use client'
export function QueryProvider({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient({
defaultOptions: { queries: { staleTime: 60 * 1000 } },
}))
return (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
```
## Server Prefetch + HydrationBoundary Pattern
```tsx
// app/posts/page.tsx (Server Component)
export default async function PostsPage() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery(postsQueryOptions())
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<PostsList />
</HydrationBoundary>
)
}
// app/posts/_components/posts-list.tsx (Client Component)
'use client'
export function PostsList() {
const { data: posts } = useQuery(postsQueryOptions()) // reads from pre-populated cache
return <ul>{posts?.map(p => <li key={p.id}>{p.title}</li>)}</ul>
}
```
## queryOptions Factory
```ts
export const postsQueryOptions = (filters?: PostFilters) =>
queryOptions({
queryKey: ['posts', 'list', filters],
queryFn: () => fetch('/api/posts').then(r => r.json()),
})
```
## Server Actions as mutationFn
```tsx
// app/posts/actions.ts
'use server'
export async function createPost(data: { title: string; body: string }) {
const post = await db.post.create({ data })
revalidatePath('/posts')
return post
}
// usage in Client Component
const mutation = useMutation({
mutationFn: createPost,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['posts', 'list'] }),
})
```
## Optimistic Updates
```tsx
const mutation = useMutation({
mutationFn: updatePost,
onMutate: async (updated) => {
await queryClient.cancelQueries({ queryKey: ['posts', 'detail', updated.id] })
const previous = queryClient.getQueryData(['posts', 'detail', updated.id])
queryClient.setQueryData(['posts', 'detail', updated.id], (old: Post) => ({ ...old, ...updated }))
return { previous }
},
onError: (_, updated, ctx) => {
queryClient.setQueryData(['posts', 'detail', updated.id], ctx?.previous)
},
onSettled: (_, __, updated) => {
queryClient.invalidateQueries({ queryKey: ['posts', 'detail', updated.id] })
},
})
```
## Key Rules
- Create a **new** `QueryClient` per request in Server Components — never reuse across requests
- Create **one** `QueryClient` per browser session via `useState` in the provider
- Always wrap server-prefetched subtrees in `HydrationBoundary`
- Mark all components using TanStack Query hooks with `'use client'`
- Never call `fetch` directly in Client Components — always go through `queryFn`
© PatrickJS · CC0-1.0 · PatrickJS/awesome-cursorrules