IDE-Tools · Foto: Homedust, CC BY 2.0
Next.js Tanstack Query
Zuletzt aktualisiert:
Typ
Regel
Lizenz
CC0 1.0
Anwendungsfeld
Cursor Rules
Voraussetzungen
Keine besonderen — direkt loslegen.
Hilft Cursor, TanStack Query korrekt mit dem Next.js App Router zu kombinieren, ohne doppeltes Fetching.
Original-Beschreibung der Autoren: Cursor rules for Next.js App Router with TanStack Query v5, covering the HydrationBoundary pattern, Server Actions as mutations, and optimistic updates.
Die Regel
---
description: "Cursor rules for Next.js App Router with TanStack Query v5, covering the HydrationBoundary pattern, Server Actions as mutations, and optimistic updates."
globs: **/*
alwaysApply: false
---
You are an expert in Next.js (App Router), TanStack Query v5, TypeScript, and combining server components with client-side data fetching.
# Next.js App Router + TanStack Query v5 Guidelines
## Architecture Philosophy
- Server Components fetch data directly (no TanStack Query needed there)
- TanStack Query lives in Client Components for interactive, real-time, or user-triggered data
- Use React Server Components for initial page data; TanStack Query for mutations, polling, and optimistic updates
- Hydrate the Query cache from server to avoid client waterfalls on first load
## Provider Setup with Hydration
```tsx
// src/providers/query-provider.tsx
'use client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { useState } from 'react'
export function QueryProvider({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(
() =>
new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
retry: (count, error: any) => error?.status !== 404 && count < 2,
},
},
}),
)
return (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
)
}
// src/app/layout.tsx
import { QueryProvider } from '@/providers/query-provider'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<QueryProvider>{children}</QueryProvider>
</body>
</html>
)
}
Hydration Pattern (Server → Client Cache)
- Prefetch in Server Components, dehydrate state, rehydrate in client
- This eliminates client-side loading states on first render
// src/app/posts/page.tsx (Server Component)
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'
import { postsQueryOptions } from '@/queries/posts'
import { PostsList } from './_components/posts-list'
export default async function PostsPage() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery(postsQueryOptions())
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<PostsList />
</HydrationBoundary>
)
}
// src/app/posts/_components/posts-list.tsx
'use client'
import { useQuery } from '@tanstack/react-query'
import { postsQueryOptions } from '@/queries/posts'
export function PostsList() {
// Reads from pre-populated cache — no loading spinner
const { data: posts } = useQuery(postsQueryOptions())
return <ul>{posts?.map(p => <li key={p.id}>{p.title}</li>)}</ul>
}
Query Definitions
// src/queries/posts.ts
import { queryOptions } from '@tanstack/react-query'
export const postKeys = {
all: ['posts'] as const,
lists: () => [...postKeys.all, 'list'] as const,
list: (filters?: PostFilters) => [...postKeys.lists(), { filters }] as const,
details: () => [...postKeys.all, 'detail'] as const,
detail: (id: string) => [...postKeys.details(), id] as const,
}
export const postsQueryOptions = (filters?: PostFilters) =>
queryOptions({
queryKey: postKeys.list(filters),
queryFn: () => fetch(`/api/posts`).then(r => r.json()),
})
export const postDetailQueryOptions = (id: string) =>
queryOptions({
queryKey: postKeys.detail(id),
queryFn: () => fetch(`/api/posts/${id}`).then(r => r.json()),
staleTime: 1000 * 60 * 5,
})
Server Actions + Mutations
- Use Next.js Server Actions as the
mutationFnin TanStack Query mutations - This gives you type-safe server mutations WITH optimistic update/rollback capabilities
// src/app/posts/actions.ts
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost(data: { title: string; body: string }) {
const post = await db.post.create({ data })
revalidatePath('/posts')
return post
}
// src/app/posts/_components/create-post-form.tsx
'use client'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { createPost } from '../actions'
import { postKeys } from '@/queries/posts'
export function CreatePostForm() {
const queryClient = useQueryClient()
const mutation = useMutation({
mutationFn: createPost,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: postKeys.lists() })
},
})
return (
<button
onClick={() => mutation.mutate({ title: 'New Post', body: '...' })}
disabled={mutation.isPending}
>
{mutation.isPending ? 'Creating...' : 'Create Post'}
</button>
)
}
Optimistic Updates with Server Actions
const mutation = useMutation({
mutationFn: updatePost,
onMutate: async (updated) => {
await queryClient.cancelQueries({ queryKey: postKeys.detail(updated.id) })
const previous = queryClient.getQueryData(postKeys.detail(updated.id))
queryClient.setQueryData(postKeys.detail(updated.id), (old: Post) => ({ ...old, ...updated }))
return { previous }
},
onError: (_, updated, ctx) => {
queryClient.setQueryData(postKeys.detail(updated.id), ctx?.previous)
},
onSettled: (_, __, updated) => {
queryClient.invalidateQueries({ queryKey: postKeys.detail(updated.id) })
},
})
When to Use Server Components vs TanStack Query
| Use Server Components When | Use TanStack Query When |
|---|---|
| Static or rarely-changing data | Real-time or frequently-updated data |
| SEO-critical initial content | User interactions (forms, toggles) |
| No need to refetch on client | Optimistic updates needed |
| Data is not shared across components | Data is shared across many components |
| No loading states desired | Fine-grained loading/error UI needed |
Route Handlers (API Routes) as Query Ta
… (hier gekürzt — Kopieren/Download liefert die vollständige Regel)
## So nutzt du sie
Die Regel kopieren (Button oben) oder als Datei herunterladen und im Projekt unter `.cursor/rules/` ablegen — Cursor lädt sie beim nächsten Start automatisch. Ältere Cursor-Versionen lesen alternativ eine einzelne `.cursorrules`-Datei im Projektstamm; dort einfach den Regel-Text ohne den Kopfblock zwischen den `---`-Zeilen einfügen.
Der Regel-Text ist englisch — Cursor versteht ihn unabhängig von der Sprache, in der Sie mit dem Editor chatten.
## Im Detail
Diese Regel löst ein spezifisches Architekturproblem des Next.js App Routers: Server-Komponenten können direkt Daten laden, TanStack Query ist aber für Client-seitiges Caching und Refetching gedacht — ohne klare Trennung entsteht leicht doppeltes Fetching oder Hydration-Fehler. Die Regel gibt vor, wann Daten serverseitig vorab geladen und dem Query-Client übergeben werden und wann Client-Komponenten selbst nachladen dürfen. Wertvoll für Apps mit viel interaktivem Nachladen, Paginierung oder Echtzeit-Updates, wo TanStack Querys Caching-Vorteile wirklich zum Tragen kommen. Für einfache, überwiegend statische Seiten reicht natives Server-Fetching meist aus.
## Praxis-Tipp
Gut geeignet für Prompts wie 'Baue eine paginierte Tabelle, die serverseitig vorgeladen und clientseitig mit TanStack Query nachgeladen wird' — genau der Fall, für den die Regel gedacht ist.
## Lizenz & Quelle
- **Lizenz:** CC0 1.0
- **Quelle:** [PatrickJS/awesome-cursorrules (GitHub)](https://github.com/PatrickJS/awesome-cursorrules)
Inhalt ansehen (nextjs-tanstack-query.mdc)
Lade …
Erfahrungen & Kommentare.
Funktioniert der Regel bei Ihnen? Tipps, Stolperfallen, Varianten — teilen Sie es mit der Community.
Lade Kommentare …
Passt dazu.
AI Agent Specialist
Cursor-Regel für diszipliniertes, klar abgegrenztes Agenten-Verhalten statt unstrukturierter KI-Antworten.
Alpha Skills Quant Factor Research
Cursor-Regel für methodisch saubere quantitative Faktor-Recherche im Trading-Umfeld.
Android Jetpack Compose
Cursor-Regel für idiomatischen, deklarativen Android-UI-Code mit Jetpack Compose.
