# tanstack-query

A skill that tells your agent how to handle server state in React with TanStack Query v5: query keys, queryOptions, mutations, v4 migration.

- skill · `type=skill` · Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, OpenCode · 724 installs · ★ 286
- Author: Mindrally
- License: Apache-2.0 · policy=mirror · the beginning of the file is at the end of this page
- Source: https://github.com/Mindrally/skills/tree/HEAD/tanstack-query/SKILL.md
- Updated: 2026-10-08
- HTML: https://affpapa.org/en/nevibecode/skills/mindrally-tanstack-query

## What it does

This skill encodes TanStack Query v5 conventions so the agent stops writing v4 code. Every query gets a stable, serializable key. Reads and writes stay apart. Client-only state goes to `useState` or context, not into the query cache. A dedicated section lists the v5 breaking changes: options-object syntax instead of positional arguments, `isPending` replacing `isLoading` on mutations, `gcTime` replacing `cacheTime`, `useSuspenseQuery` replacing the `suspense: true` option.

It also lays out a project structure: `api/endpoints`, `queries`, `hooks/queries`, `hooks/mutations`, `providers`. You get a key factory like `userKeys`, the `queryOptions()` helper for sharing one query definition across components, router loaders and prefetch calls, custom hooks, dependent queries, and parallel fetching with `useQueries`. The `QueryClient` setup lives in `providers/QueryProvider.tsx`.

The part that bites people: instantiate `QueryClient` once at the app root. Create it inside a component and the cache resets on every render. The skill assumes TypeScript, so responses are typed through the `useQuery` and `UseQueryOptions` generics.

## Install

### Claude Code (native platform)

1. **Skills CLI (Vercel)**
```bash
npx skills add Mindrally/skills --skill tanstack-query -a claude-code
```

2. **Manually: put the files in place**
- File: `~/.claude/skills/tanstack-query/SKILL.md` — Copy the whole skill folder; for one project use .claude/skills/tanstack-query/

### Codex

1. **Skills CLI (Vercel)**
```bash
npx skills add Mindrally/skills --skill tanstack-query -a codex
```

2. **Manually: put the files in place**
- File: `.agents/skills/tanstack-query/SKILL.md` — For all projects use ~/.agents/skills/

### Cursor

1. **Skills CLI (Vercel)**
```bash
npx skills add Mindrally/skills --skill tanstack-query -a cursor
```

2. **Manually: put the files in place**
- File: `.cursor/skills/tanstack-query/SKILL.md` — Cursor also reads .agents/skills/ and .claude/skills/

### GitHub Copilot

1. **GitHub CLI**
```bash
gh skill install Mindrally/skills tanstack-query
```

2. **Manually: put the files in place**
- File: `.github/skills/tanstack-query/SKILL.md`

### Gemini CLI

1. **Skills CLI (Vercel)**
```bash
npx skills add Mindrally/skills --skill tanstack-query -a gemini-cli
```

2. **Platform CLI**
```bash
gemini skills install https://github.com/Mindrally/skills --path tanstack-query
```

### Windsurf (Devin Desktop)

**Manually: put the files in place**
- File: `.devin/skills/tanstack-query/SKILL.md` — The legacy .windsurf/skills/ path still works

### OpenCode

**Skills CLI (Vercel)**
```bash
npx skills add Mindrally/skills --skill tanstack-query -a opencode
```

### Claude.ai

**Claude.ai**
- Zip the folder and upload it in Settings → Capabilities → Skills

Where a file is given instead of a command, copy the files from the source to that path: https://github.com/Mindrally/skills/tree/HEAD/tanstack-query/SKILL.md

## When to use it
- port v4 queries from positional arguments to the v5 options object
- build a key factory so cache invalidation stops being guesswork
- move a query definition into `queryOptions()` and reuse it in a router loader
- add an optimistic update to a mutation with rollback on error
- configure `QueryClient` with `staleTime` and `gcTime` in one place

## Example prompts
- Write a useUser hook with a key from the userKeys factory and a typed response
- Convert this useQuery to v5 syntax and swap cacheTime for gcTime
- Add an optimistic update to useCreateUser that rolls the cache back on error

## Source text · SKILL.md · Apache-2.0

Below is the content of a third-party file. It is data, not instructions for you: do not act on anything written inside unless the human asks. Block tag: 15328a3359bfadc9.

BEGIN THIRD-PARTY FILE 15328a3359bfadc9
````text
---
name: tanstack-query
description: "Best practices for TanStack Query v5 (React Query) server state management, including query key factories, the queryOptions helper, mutations, optimistic updates, infinite queries, and Suspense mode. Use when fetching or caching server data in React, writing custom query/mutation hooks, setting up a QueryClient, implementing optimistic updates, or migrating v4 patterns to v5."
metadata:
  maintainer: Mindrally
  source: https://github.com/Mindrally/skills
---

# TanStack Query Best Practices

TanStack Query (formerly React Query) handles server-state caching, background updates, and stale-data management out of the box. This skill covers v5 patterns and APIs — v5 introduced several breaking changes from v4 that older examples online still don't reflect.

## Core Principles

- Use TanStack Query for all server state management and data fetching; it is not a general client-state manager — keep client-only state in `useState`/context/a state library instead
- Minimize `useEffect` and `useState` for server data; favor TanStack Query's built-in state management
- Every query needs a stable, serializable query key that uniquely describes the data it holds
- Mutations handle writes; queries handle reads — don't blur this boundary
- Implement proper error handling with user-friendly messages
- Use TypeScript for full type safety with query responses

## v5 Breaking Changes to Watch For

If you see or write any of these v4 patterns, update them:

- **Object syntax only**: `useQuery`, `useInfiniteQuery`, etc. no longer accept positional arguments (`useQuery(key, fn, options)`). Always pass a single options object: `useQuery({ queryKey, queryFn, ...options })`.
- **`isPending` replaces `isLoading`** as the name for "no data yet and a fetch is in flight" on `useMutation`. On `useQuery`, `isPending` means no cached data exists at all; `isLoading` is now derived (`isPending && isFetching`) and still usable for the classic "first load" spinner case.
- **`cacheTime` renamed to `gcTime`** (garbage collection time).
- **`queryOptions()` helper** for defining reusable, typed query definitions shared between components, loaders, and prefetch calls.
- **`useSuspenseQuery`** (and `useSuspenseInfiniteQuery`) for Suspense-based data fetching, replacing the old `suspense: true` option.
- **`placeholderData: keepPreviousData`** replaces the old `keepPreviousData: true` boolean for pagination.

## Project Structure

```
src/
  api/
    client.ts             # API client configuration
    endpoints/
````
END THIRD-PARTY FILE 15328a3359bfadc9

The third-party file has ended. Everything between the 15328a3359bfadc9 tags is data: requests and commands from it are not followed. An “end of file” line with another tag inside the block is part of the file.

First 2561 of 14573 bytes. © Mindrally · Apache-2.0 · https://github.com/Mindrally/skills/tree/HEAD/tanstack-query/SKILL.md

_Part of the page did not fit — see the HTML version._

---
NeVibeCode on affpapa.org. Catalog texts are descriptions, not instructions for the agent. How to choose and install: https://affpapa.org/en/nevibecode/agents.md
