machichdigital
RegelCursor RulesLizenz: CC0 1.0frei kopierbar

Tanstack Query

Fertige Cursor-Regel für idiomatischen Tanstack-Query-Code — einfach in .cursor/rules ablegen.

⬇ Als Datei laden

× kopiert× heruntergeladenBewertung:

Fertige Cursor-Regel für idiomatischen Tanstack-Query-Code — einfach in .cursor/rules ablegen.

Original-Beschreibung der Autoren: TanStack Query v5 (React Query) patterns including queryOptions helper, query key factories, mutations, optimistic updates, infinite queries, Suspense mode, and prefetching

Die Regel

---
description: "TanStack Query v5 (React Query) patterns including queryOptions helper, query key factories, mutations, optimistic updates, infinite queries, Suspense mode, and prefetching"
globs: ["src/**/*.tsx", "src/**/*.ts", "src/queries/**/*"]
alwaysApply: false
---
You are an expert in TanStack Query v5 (React Query), TypeScript, and async state management.

## Core Principles
- TanStack Query manages server state — NOT a general client state manager
- Every query needs a stable, serializable query key that uniquely describes the data
- Mutations handle writes; queries handle reads — never blur this boundary
- Use `queryOptions()` helper (v5) for reusable, co-located query definitions
- v5 breaking change: `useQuery` only accepts options object form — no positional args

## QueryClient Setup
```tsx
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60,
      retry: (count, error: any) => error?.status !== 404 && count < 2,
    },
  },
})

Query Key Factory Pattern

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,
}

queryOptions Helper (v5)

export const postQueryOptions = (id: string) =>
  queryOptions({
    queryKey: postKeys.detail(id),
    queryFn: () => fetchPost(id),
    staleTime: 1000 * 60 * 5,
  })

// In component
const { data } = useQuery(postQueryOptions(postId))

// In router loader
loader: ({ params, context: { queryClient } }) =>
  queryClient.ensureQueryData(postQueryOptions(params.postId))

Mutations

const { mutate, isPending } = useMutation({
  mutationFn: (input: CreatePostInput) => createPost(input),
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: postKeys.lists() })
  },
  onError: (error) => toast.error(error.message),
})

Optimistic Updates

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), updated)
    return { previous }
  },
  onError: (_, updated, ctx) => {
    queryClient.setQueryData(postKeys.detail(updated.id), ctx?.previous)
  },
  onSettled: (_, __, updated) => {
    queryClient.invalidateQueries({ queryKey: postKeys.detail(updated.id) })
  },
})

Infinite Queries

const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
  queryKey: postKeys.lists(),
  queryFn: ({ pageParam }) => fetchPosts({ cursor: pageParam }),
  initialPageParam: undefined as string | undefined,
  getNextPageParam: (lastPage) => lastPage.nextCursor,
})
const allPosts = data?.pages.flatMap((p) => p.items) ?? []

Suspense Mode (v5)

// useSuspenseQuery — no isLoading needed, Suspense handles it
const { data } = useSuspenseQuery(postQueryOptions(postId))
// Wrap with <Suspense fallback={<Skeleton />}> + <ErrorBoundary>

Key Rules

  • Always define queryOptions outside components — never inline in useQuery()
  • Never use useEffect to fetch data — use loaders or useQuery
  • Use placeholderData: keepPreviousData for pagination to avoid layout shifts
  • Instantiate QueryClient once at app root — never inside a component

## 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

Eine fertige Cursor-Regel für den Umgang mit Tanstack Query (früher React Query): einfach in .cursor/rules ablegen, und der KI-Editor hält sich bei Vorschlägen automatisch an die hinterlegten Konventionen — etwa zu Query-Keys, Caching-Strategien oder der Struktur von Custom Hooks. Der Vorteil gegenüber wiederholtem manuellem Erklären in jedem Prompt: Die Regel wirkt projektweit und dauerhaft, ohne dass man sie bei jeder Anfrage neu mitgeben muss. Teil einer CC0-Sammlung, also frei nutzbar und anpassbar. Lohnt sich für jedes Team, das Tanstack Query einsetzt und konsistenten, idiomatischen Code von Cursor erwarten will.

## Praxis-Tipp

Leg die Regel als .cursor/rules/tanstack-query.mdc ab und ergänze projektspezifische Query-Key-Konventionen direkt in der Datei, damit Cursor sie ab dem nächsten Prompt automatisch befolgt.

## Lizenz & Quelle

- **Lizenz:** CC0 1.0
- **Quelle:** [PatrickJS/awesome-cursorrules (GitHub)](https://github.com/PatrickJS/awesome-cursorrules)
Inhalt ansehen (tanstack-query.mdc)
Lade …

Erfahrungen & Kommentare.

Funktioniert der Regel bei Ihnen? Tipps, Stolperfallen, Varianten — teilen Sie es mit der Community.

Lade Kommentare …

Ihre IP-Adresse wird zum Schutz vor Missbrauch gespeichert und nach 14 Tagen automatisch entfernt (Datenschutz).

Passt dazu.