TanStack Query Development
Use this capability whenever you write or review code that reads or writes server state — data another system owns, which the client holds only as a cache. It owns how that data is organized: where a query or mutation is defined, what identifies it in the cache, when it refetches, what invalidates it, how a failure surfaces, and how all of it is tested.
Server state is not application state. It goes stale on its own, it can be refetched, and two components asking for it should get one request. That is the whole reason this layer exists, and the rules below follow from it.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
Baseline and Scope
TanStack Query v5 is the baseline, verified against 5.101.4 on 2026-08-02 against the React adapter's documentation. React 18 and TypeScript 5.4 are the minimums. Types ship as patch releases rather than majors, so a project should pin the patch version and expect type changes between them.
The React adapter is the only one in scope. The Vue, Svelte, Solid, Angular, Lit, and Preact adapters version independently of it and are out of scope entirely — no rule, no comparison.
Both web and React Native are in scope. Native needs wiring the browser gets for free; react-native.md covers it, and skipping it silently disables two defaults rather than failing loudly.
Two references are conditional. server-rendering.md applies only where the host renders on a server or uses React Server Components; offline-persistence.md applies only where the host's requirements call for offline continuity. Load neither by default.
Version-sensitive claims name their version. Behaviour has moved inside v5 — the mutation callback signatures gained an onMutateResult and a context argument, and staleTime: 'static', subscribed, and environmentManager are all recent additions. Treat an unversioned claim about this library, here or anywhere else, as suspect and check it against the installed version's documentation.
Boundaries
This skill stops where another capability owns the subject:
- A component's own mechanics — composition, props contracts, extracted hooks, state placement, memoization, loading and error surfaces, list virtualization, test-hook naming — belong to a React component development capability. This skill states only what a component owes a query it consumes.
- CSS, design tokens, and themes belong to a React component styling capability.
- Framework routing, rendering, directives, route handlers, and on-device storage mechanisms belong to the matching app-framework capability. Where one of those forbids hand-rolling a cache or requires a request to be cancelled on teardown, this skill supplies the mechanism and names that capability as owner of the trigger.
- Log levels, capture semantics, breadcrumbs, and PII boundaries belong to a software instrumentation capability. This skill owns only where a failure is caught, surfaced, and declared — see the
meta channel in error-handling.md — not what a logger or error tracker does with it afterwards.
- Assertion design, fixture quality, and coverage judgment belong to unit-testing and quality-assurance capabilities. testing.md owns only what is specific to testing this library.
The host project's existing convention wins. Every rule states a default for a project that has not decided yet. Where the surrounding codebase already answers a question — the directory layout, the retry count, the error channel — match what is there rather than migrating the codebase as a side effect of an unrelated change.
Server-State Boundary
See server-state-boundary.md for:
- deciding whether a piece of data belongs to this layer, a store library, or a component
- recognizing a lifecycle or effect hook that looks like a query but is not one
- what remains in a global store once server state moves out of it
Option Factories
See option-factories.md for:
- placing a query or mutation definition under its feature, and naming the file and the function
- returning one of the three option helpers, and why a bespoke wrapper hook is not an alternative
- keeping a factory a pure builder that calls no hook and starts no fetch
- deciding what a factory accepts as input, and what it reads at call time instead
Query Keys
See query-keys.md for:
- putting the tenant at the root of a key when more than one can exist at a time
- shaping the resource path beneath it, and distinguishing a list from a single item
- placing filters, sorting, and pagination in the key
- deciding which inputs belong in the key and which deliberately stay out
- deriving every cache read and invalidation from the factory rather than retyping the array
- registering a project-wide key type
Query Functions
See query-functions.md for:
- the promise contract, and the one return value that is treated as a failure
- making a transport that does not throw on an error response behave like one
- what the function receives, and reading a store or singleton without a hook
- threading the abort signal, cancelling manually, and where the teardown trigger is owned
- streaming a response that arrives in chunks
Consuming a Query
See consuming-queries.md for:
- calling a factory from a component, and layering a per-call option onto it
- telling the two status fields apart, and picking the one a spinner should read
- rendering the loading, error, empty, and loaded branches
- narrowing what a component subscribes to, and the two destructuring habits that defeat it
- reading a query result inside a React hook dependency array
- the Suspense variants, what they give up, and what to do when the key changes
Cache Lifetime
See cache-lifetime.md for:
- the defaults that surprise a first-time reader, and which of them to change deliberately
- choosing a staleness window, including the two values that stop refetching entirely
- how long an unused entry survives, and how that interacts with staleness
- the three automatic refetch triggers and turning one down
- polling on a timer, adapting the interval, and stopping it
- what belongs on the shared client versus on one factory
Fetch Orchestration
See fetch-orchestration.md for:
- gating a query on a condition, and the type-safe alternative to a boolean gate
- one query waiting on another, and why that is a cost rather than a pattern
- running a set of queries whose size changes between renders
- recognizing a request waterfall and the three ways to flatten one
- warming the cache ahead of a render, and which warming call returns data or throws
Seeding the Cache
See seeding-the-cache.md for:
- choosing between data that is written to the cache and data that only stands in for it
- telling the cache how old the seed already is
- building a seed out of a list query a detail view is opening from
- writing a value directly when it is already in hand
Paginated and Infinite Queries
See paginated-and-infinite-queries.md for:
- defining a page-walking query, and the property order its type inference depends on
- deciding there is no next page, and capping how many are retained
- driving "load more" from a list without racing the fetch already in flight
- flattening pages for rendering, and what a refetch costs once several are loaded
- keeping the previous page visible while a page-numbered query moves
Mutations
See mutations.md for:
- defining a write, typing its variables, and keying it
- triggering it, and choosing between the callback form and the promise form
- deciding whether a callback belongs to the operation or to the screen that fired it
- the callback signatures and the property order inference depends on
- clearing a finished mutation's state, and serializing writes that must not interleave
Cache Invalidation
See cache-invalidation.md for:
- evicting what a write made stale, at the narrowest scope that is still correct
- matching by prefix, exactly, or by predicate
- holding a mutation pending until the refetch it triggered finishes
- writing a response straight into the cache instead of refetching it
- choosing among invalidate, refetch, reset, and remove
- clearing a session's entries when it ends
Optimistic Updates
See optimistic-updates.md for:
- showing a pending write without touching the cache
- writing to the cache ahead of the server, and rolling back when it fails
- stopping an in-flight refetch from overwriting the optimistic value
- reaching a pending mutation from a component that did not fire it
- choosing between the two approaches
Error Handling
See error-handling.md for:
- making a failure reach the query at all, and the callbacks v5 removed
- choosing one error channel per query and what each one suits
- resetting an error boundary so a failed query can be retried
- deciding what a mutation swallows and what it lets through
- keeping user-facing copy out of the data layer
- declaring a failure's reporting policy on the query itself
- setting a retry policy, and what must never be retried
The Query Client
See query-client.md for:
- creating exactly one client, and the two correct ways to hold it
- what belongs in the client's defaults versus on an individual factory
- reaching the client from inside a factory versus from inside a component
- setting defaults for a family of keys
TypeScript
See typescript.md for:
- where inference comes from, and the one function that has to be typed for it to work
- narrowing a result to get at its data
- typing the error, and narrowing a domain subclass at the call site
- registering project-wide types for errors, metadata, and keys
- the cache-read helper that infers and the one that does not
Testing
See testing.md for:
- giving each test its own client, and the
retry: false that keeps an error path from timing out
- what to mock, and the layer that must never be mocked
- asserting on an asynchronous result, and covering each state branch
- testing an option factory on its own
- exercising a page-walking query, and rendering a route that owns one
Tooling and Versions
See tooling-and-versions.md for:
- the eight lint rules, each as a rule a reader can follow without the linter
- when
@tanstack/eslint-plugin-query is worth installing, and why mutation-property-order stays a review check
- inspecting the cache while diagnosing, on web and elsewhere
- what changed on the way to v5, for a codebase arriving from v4
- reading documentation at the version actually installed
React Native
See react-native.md for:
- connecting the two managers that the browser populates automatically
- what silently stops working when they are not connected
- refetching when a screen regains focus, and unsubscribing one that loses it
- fetching on a device whose network drops more often than a browser's
Server Rendering
Load only where the host renders on a server or uses React Server Components.
See server-rendering.md for:
- creating a client per request on the server and one for the browser session
- the defaults that change when rendering starts on a server
- moving cache entries across the boundary, and what is excluded or redacted
- fetching in a server component and consuming the result in a client one
- where this skill stops and the app-framework capability takes over
Offline Persistence
Load only where the host's requirements call for offline continuity.
See offline-persistence.md for:
- choosing how the layer behaves with no connection, and the state a paused fetch reports
- writes that queue while offline and resume in order
- persisting the cache, restoring it, and rendering while a restore is in flight
- why persistence here is not the hand-rolled cache an app-framework capability forbids
- invalidating a persisted cache on release, and keeping one tenant's data out of another's session
1---2name: tanstack-query-development3description: Writing or reviewing anything that reads or writes server state through TanStack Query — the v5 server-state layer, owning cache identity, refetch timing, invalidation, and how a failure surfaces. Triggers on "useQuery", "useMutation", "queryOptions", "queryKey", "staleTime", "invalidateQueries", "setQueryData", "optimistic update", "infinite scroll", "QueryClient", a stale list after a write, a colliding cache entry, or a refetch that fires too often or never. For a component's own composition, props, and state use a React component development capability; for routing and rendering the matching app-framework one.4---56# TanStack Query Development78Use this capability whenever you write or review code that reads or writes **server state** — data another system owns, which the client holds only as a cache. It owns how that data is organized: where a query or mutation is defined, what identifies it in the cache, when it refetches, what invalidates it, how a failure surfaces, and how all of it is tested.910Server state is not application state. It goes stale on its own, it can be refetched, and two components asking for it should get one request. That is the whole reason this layer exists, and the rules below follow from it.1112The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119.html).1314## Baseline and Scope1516**TanStack Query v5 is the baseline**, verified against **5.101.4** on **2026-08-02** against the [React adapter's documentation](https://tanstack.com/query/latest/docs/framework/react/overview). React 18 and TypeScript 5.4 are the minimums. Types ship as **patch** releases rather than majors, so a project should pin the patch version and expect type changes between them.1718**The React adapter is the only one in scope.** The Vue, Svelte, Solid, Angular, Lit, and Preact adapters version independently of it and are out of scope entirely — no rule, no comparison.1920**Both web and React Native are in scope.** Native needs wiring the browser gets for free; [react-native.md](./references/react-native.md) covers it, and skipping it silently disables two defaults rather than failing loudly.2122**Two references are conditional.** [server-rendering.md](./references/server-rendering.md) applies only where the host renders on a server or uses React Server Components; [offline-persistence.md](./references/offline-persistence.md) applies only where the host's requirements call for offline continuity. Load neither by default.2324**Version-sensitive claims name their version.** Behaviour has moved inside v5 — the mutation callback signatures gained an `onMutateResult` and a `context` argument, and `staleTime: 'static'`, `subscribed`, and `environmentManager` are all recent additions. Treat an unversioned claim about this library, here or anywhere else, as suspect and check it against the installed version's documentation.2526## Boundaries2728This skill stops where another capability owns the subject:2930- **A component's own mechanics** — composition, props contracts, extracted hooks, state placement, memoization, loading and error surfaces, list virtualization, test-hook naming — belong to a React component development capability. This skill states only what a component owes a query it consumes.31- **CSS, design tokens, and themes** belong to a React component styling capability.32- **Framework routing, rendering, directives, route handlers, and on-device storage mechanisms** belong to the matching app-framework capability. Where one of those forbids hand-rolling a cache or requires a request to be cancelled on teardown, this skill supplies the mechanism and names that capability as owner of the trigger.33- **Log levels, capture semantics, breadcrumbs, and PII boundaries** belong to a software instrumentation capability. This skill owns only where a failure is caught, surfaced, and **declared** — see the `meta` channel in [error-handling.md](./references/error-handling.md) — not what a logger or error tracker does with it afterwards.34- **Assertion design, fixture quality, and coverage judgment** belong to unit-testing and quality-assurance capabilities. [testing.md](./references/testing.md) owns only what is specific to testing this library.3536**The host project's existing convention wins.** Every rule states a default for a project that has not decided yet. Where the surrounding codebase already answers a question — the directory layout, the retry count, the error channel — match what is there rather than migrating the codebase as a side effect of an unrelated change.3738## Server-State Boundary3940See [server-state-boundary.md](./references/server-state-boundary.md) for:4142- deciding whether a piece of data belongs to this layer, a store library, or a component43- recognizing a lifecycle or effect hook that looks like a query but is not one44- what remains in a global store once server state moves out of it4546## Option Factories4748See [option-factories.md](./references/option-factories.md) for:4950- placing a query or mutation definition under its feature, and naming the file and the function51- returning one of the three option helpers, and why a bespoke wrapper hook is not an alternative52- keeping a factory a pure builder that calls no hook and starts no fetch53- deciding what a factory accepts as input, and what it reads at call time instead5455## Query Keys5657See [query-keys.md](./references/query-keys.md) for:5859- putting the tenant at the root of a key when more than one can exist at a time60- shaping the resource path beneath it, and distinguishing a list from a single item61- placing filters, sorting, and pagination in the key62- deciding which inputs belong in the key and which deliberately stay out63- deriving every cache read and invalidation from the factory rather than retyping the array64- registering a project-wide key type6566## Query Functions6768See [query-functions.md](./references/query-functions.md) for:6970- the promise contract, and the one return value that is treated as a failure71- making a transport that does not throw on an error response behave like one72- what the function receives, and reading a store or singleton without a hook73- threading the abort signal, cancelling manually, and where the teardown trigger is owned74- streaming a response that arrives in chunks7576## Consuming a Query7778See [consuming-queries.md](./references/consuming-queries.md) for:7980- calling a factory from a component, and layering a per-call option onto it81- telling the two status fields apart, and picking the one a spinner should read82- rendering the loading, error, empty, and loaded branches83- narrowing what a component subscribes to, and the two destructuring habits that defeat it84- reading a query result inside a React hook dependency array85- the Suspense variants, what they give up, and what to do when the key changes8687## Cache Lifetime8889See [cache-lifetime.md](./references/cache-lifetime.md) for:9091- the defaults that surprise a first-time reader, and which of them to change deliberately92- choosing a staleness window, including the two values that stop refetching entirely93- how long an unused entry survives, and how that interacts with staleness94- the three automatic refetch triggers and turning one down95- polling on a timer, adapting the interval, and stopping it96- what belongs on the shared client versus on one factory9798## Fetch Orchestration99100See [fetch-orchestration.md](./references/fetch-orchestration.md) for:101102- gating a query on a condition, and the type-safe alternative to a boolean gate103- one query waiting on another, and why that is a cost rather than a pattern104- running a set of queries whose size changes between renders105- recognizing a request waterfall and the three ways to flatten one106- warming the cache ahead of a render, and which warming call returns data or throws107108## Seeding the Cache109110See [seeding-the-cache.md](./references/seeding-the-cache.md) for:111112- choosing between data that is written to the cache and data that only stands in for it113- telling the cache how old the seed already is114- building a seed out of a list query a detail view is opening from115- writing a value directly when it is already in hand116117## Paginated and Infinite Queries118119See [paginated-and-infinite-queries.md](./references/paginated-and-infinite-queries.md) for:120121- defining a page-walking query, and the property order its type inference depends on122- deciding there is no next page, and capping how many are retained123- driving "load more" from a list without racing the fetch already in flight124- flattening pages for rendering, and what a refetch costs once several are loaded125- keeping the previous page visible while a page-numbered query moves126127## Mutations128129See [mutations.md](./references/mutations.md) for:130131- defining a write, typing its variables, and keying it132- triggering it, and choosing between the callback form and the promise form133- deciding whether a callback belongs to the operation or to the screen that fired it134- the callback signatures and the property order inference depends on135- clearing a finished mutation's state, and serializing writes that must not interleave136137## Cache Invalidation138139See [cache-invalidation.md](./references/cache-invalidation.md) for:140141- evicting what a write made stale, at the narrowest scope that is still correct142- matching by prefix, exactly, or by predicate143- holding a mutation pending until the refetch it triggered finishes144- writing a response straight into the cache instead of refetching it145- choosing among invalidate, refetch, reset, and remove146- clearing a session's entries when it ends147148## Optimistic Updates149150See [optimistic-updates.md](./references/optimistic-updates.md) for:151152- showing a pending write without touching the cache153- writing to the cache ahead of the server, and rolling back when it fails154- stopping an in-flight refetch from overwriting the optimistic value155- reaching a pending mutation from a component that did not fire it156- choosing between the two approaches157158## Error Handling159160See [error-handling.md](./references/error-handling.md) for:161162- making a failure reach the query at all, and the callbacks v5 removed163- choosing one error channel per query and what each one suits164- resetting an error boundary so a failed query can be retried165- deciding what a mutation swallows and what it lets through166- keeping user-facing copy out of the data layer167- declaring a failure's reporting policy on the query itself168- setting a retry policy, and what must never be retried169170## The Query Client171172See [query-client.md](./references/query-client.md) for:173174- creating exactly one client, and the two correct ways to hold it175- what belongs in the client's defaults versus on an individual factory176- reaching the client from inside a factory versus from inside a component177- setting defaults for a family of keys178179## TypeScript180181See [typescript.md](./references/typescript.md) for:182183- where inference comes from, and the one function that has to be typed for it to work184- narrowing a result to get at its data185- typing the error, and narrowing a domain subclass at the call site186- registering project-wide types for errors, metadata, and keys187- the cache-read helper that infers and the one that does not188189## Testing190191See [testing.md](./references/testing.md) for:192193- giving each test its own client, and the `retry: false` that keeps an error path from timing out194- what to mock, and the layer that must never be mocked195- asserting on an asynchronous result, and covering each state branch196- testing an option factory on its own197- exercising a page-walking query, and rendering a route that owns one198199## Tooling and Versions200201See [tooling-and-versions.md](./references/tooling-and-versions.md) for:202203- the eight lint rules, each as a rule a reader can follow without the linter204- when `@tanstack/eslint-plugin-query` is worth installing, and why `mutation-property-order` stays a review check205- inspecting the cache while diagnosing, on web and elsewhere206- what changed on the way to v5, for a codebase arriving from v4207- reading documentation at the version actually installed208209## React Native210211See [react-native.md](./references/react-native.md) for:212213- connecting the two managers that the browser populates automatically214- what silently stops working when they are not connected215- refetching when a screen regains focus, and unsubscribing one that loses it216- fetching on a device whose network drops more often than a browser's217218## Server Rendering219220Load only where the host renders on a server or uses React Server Components.221222See [server-rendering.md](./references/server-rendering.md) for:223224- creating a client per request on the server and one for the browser session225- the defaults that change when rendering starts on a server226- moving cache entries across the boundary, and what is excluded or redacted227- fetching in a server component and consuming the result in a client one228- where this skill stops and the app-framework capability takes over229230## Offline Persistence231232Load only where the host's requirements call for offline continuity.233234See [offline-persistence.md](./references/offline-persistence.md) for:235236- choosing how the layer behaves with no connection, and the state a paused fetch reports237- writes that queue while offline and resume in order238- persisting the cache, restoring it, and rendering while a restore is in flight239- why persistence here is not the hand-rolled cache an app-framework capability forbids240- invalidating a persisted cache on release, and keeping one tenant's data out of another's session