File: README.md
TanStack Query
An async state management library built to simplify fetching, caching, synchronizing, and updating server state.
- Protocolβagnostic fetching (REST, GraphQL, promises, etc.)
- Caching, refetching, pagination & infinite scroll
- Mutations, dependent queries & background updates
- Prefetching, cancellation & React Suspense support
[Read the docs β](https://tanstack.com/query)
Get Involved
- We welcome issues and pull requests!
- Participate in GitHub discussions
- Chat with the community on Discord
- See CONTRIBUTING.md for setup instructions
Partners
Explore the TanStack Ecosystem
- TanStack Config β Tooling for JS/TS packages
- TanStack DB β Reactive sync client store
- TanStack DevTools β Unified devtools panel
- TanStack Form β Typeβsafe form state
- TanStack Pacer β Debouncing, throttling, batching
- TanStack Query β Async state & caching
- TanStack Ranger β Range & slider primitives
- TanStack Router β Typeβsafe routing, caching & URL state
- TanStack Start β Fullβstack SSR & streaming
- TanStack Store β Reactive data store
- TanStack Table β Headless datagrids
- TanStack Virtual β Virtualized rendering
β¦ and more at TanStack.com Β»
File: docs/eslint/eslint-plugin-query.md
id: eslint-plugin-query title: ESLint Plugin Query
TanStack Query comes with its own ESLint plugin. This plugin is used to enforce best practices and to help you avoid common mistakes.
Installation
The plugin is a separate package that you need to install:
npm i -D @tanstack/eslint-plugin-queryor
pnpm add -D @tanstack/eslint-plugin-queryor
yarn add -D @tanstack/eslint-plugin-queryor
bun add -D @tanstack/eslint-plugin-queryFlat Config (`eslint.config.js`)
Recommended setup
To enable all of the recommended rules for our plugin, add the following config:
import pluginQuery from '@tanstack/eslint-plugin-query'
export default [
...pluginQuery.configs['flat/recommended'],
// Any other config...
]Recommended strict setup
The flat/recommended-strict config extends flat/recommended with additional opinionated rules that enforce best practices more aggressively.
import pluginQuery from '@tanstack/eslint-plugin-query'
export default [
...pluginQuery.configs['flat/recommended-strict'],
// Any other config...
]Custom setup
Alternatively, you can load the plugin and configure only the rules you want to use:
import pluginQuery from '@tanstack/eslint-plugin-query'
export default [
{
plugins: {
'@tanstack/query': pluginQuery,
},
rules: {
'@tanstack/query/exhaustive-deps': 'error',
},
},
// Any other config...
]Legacy Config (`.eslintrc`)
Recommended setup
To enable all of the recommended rules for our plugin, add plugin:@tanstack/query/recommended in extends:
{
"extends": ["plugin:@tanstack/query/recommended"]
}Recommended strict setup
The recommendedStrict config extends recommended with additional opinionated rules:
{
"extends": ["plugin:@tanstack/query/recommendedStrict"]
}Custom setup
Alternatively, add @tanstack/query to the plugins section, and configure the rules you want to use:
{
"plugins": ["@tanstack/query"],
"rules": {
"@tanstack/query/exhaustive-deps": "error"
}
}Rules
- @tanstack/query/exhaustive-deps
- @tanstack/query/no-rest-destructuring
- @tanstack/query/stable-query-client
- @tanstack/query/no-unstable-deps
- @tanstack/query/infinite-query-property-order
- @tanstack/query/no-void-query-fn
- @tanstack/query/mutation-property-order
- @tanstack/query/prefer-query-options
File: docs/eslint/exhaustive-deps.md
id: exhaustive-deps title: Exhaustive dependencies for query keys
Query keys should contain the serializable values that identify the data returned by your queryFn.
This makes sure that queries are cached independently and that queries are refetched automatically when those values change.
Function call targets are not query key dependencies. For example, fetchTodoById(todoId) needs todoId in the query key, but not fetchTodoById. Values referenced inside nested callbacks are still dependencies, so promise.then(() => todoId) also needs todoId in the query key.
Rule Details
Examples of incorrect code for this rule:
/* eslint "@tanstack/query/exhaustive-deps": "error" */
useQuery({
queryKey: ['todo'],
queryFn: () => api.getTodo(todoId),
})
const todoQueries = {
detail: (id) => ({ queryKey: ['todo'], queryFn: () => api.getTodo(id) }),
}Examples of correct code for this rule:
const Component = ({ todoId }) => {
const todos = useTodos()
useQuery({
queryKey: ['todo', todoId],
queryFn: () => todos.getTodo(todoId),
})
}const todos = createTodos()
const todoQueries = {
detail: (id) => ({
queryKey: ['todo', id],
queryFn: () => todos.getTodo(id),
}),
}// with { allowlist: { types: ["Config"] }}
class Config { ... }
const Component = ({ todoId, config }: { todoId: string, config: Config }) => {
useQuery({
queryKey: ['todo', todoId],
queryFn: () => fetchTodo(todoId, config.baseUrl),
})
}Options
allowlist.variables: An array of variable names that should be ignored when checking dependenciesallowlist.types: An array of TypeScript type names that should be ignored when checking dependencies
{
"@tanstack/query/exhaustive-deps": [
"error",
{
"allowlist": {
"variables": ["api", "config"],
"types": ["ApiClient", "Config"]
}
}
]
}When Not To Use It
If you don't care about the rules of the query keys, then you will not need this rule.
Attributes
- β Recommended
- π§ Fixable
File: docs/eslint/infinite-query-property-order.md
id: infinite-query-property-order title: Ensure correct order of inference sensitive properties for infinite queries
For the following functions, the property order of the passed in object matters due to type inference:
useInfiniteQueryuseSuspenseInfiniteQueryinfiniteQueryOptions
The correct property order is as follows:
queryFngetPreviousPageParamgetNextPageParam
All other properties are insensitive to the order as they do not depend on type inference.
Rule Details
Examples of incorrect code for this rule:
/* eslint "@tanstack/query/infinite-query-property-order": "warn" */
import { useInfiniteQuery } from '@tanstack/react-query'
const query = useInfiniteQuery({
queryKey: ['projects'],
getNextPageParam: (lastPage) => lastPage.nextId ?? undefined,
queryFn: async ({ pageParam }) => {
const response = await fetch(`/api/projects?cursor=${pageParam}`)
return await response.json()
},
initialPageParam: 0,
getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined,
maxPages: 3,
})Examples of correct code for this rule:
/* eslint "@tanstack/query/infinite-query-property-order": "warn" */
import { useInfiniteQuery } from '@tanstack/react-query'
const query = useInfiniteQuery({
queryKey: ['projects'],
queryFn: async ({ pageParam }) => {
const response = await fetch(`/api/projects?cursor=${pageParam}`)
return await response.json()
},
initialPageParam: 0,
getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined,
getNextPageParam: (lastPage) => lastPage.nextId ?? undefined,
maxPages: 3,
})Attributes
- β Recommended
- π§ Fixable
File: docs/eslint/mutation-property-order.md
id: mutation-property-order title: Ensure correct order of inference-sensitive properties in useMutation()
For the following functions, the property order of the passed in object matters due to type inference:
useMutation()
The correct property order is as follows:
onMutateonErroronSettled
All other properties are insensitive to the order as they do not depend on type inference.
Rule Details
Examples of incorrect code for this rule:
/* eslint "@tanstack/query/mutation-property-order": "warn" */
import { useMutation } from '@tanstack/react-query'
const mutation = useMutation({
mutationFn: () => Promise.resolve('success'),
onSettled: () => {
results.push('onSettled-promise')
return Promise.resolve('also-ignored') // Promise<string> (should be ignored)
},
onMutate: async () => {
results.push('onMutate-async')
await sleep(1)
return { backup: 'async-data' }
},
onError: async () => {
results.push('onError-async-start')
await sleep(1)
results.push('onError-async-end')
},
})Examples of correct code for this rule:
/* eslint "@tanstack/query/mutation-property-order": "warn" */
import { useMutation } from '@tanstack/react-query'
const mutation = useMutation({
mutationFn: () => Promise.resolve('success'),
onMutate: async () => {
results.push('onMutate-async')
await sleep(1)
return { backup: 'async-data' }
},
onError: async () => {
results.push('onError-async-start')
await sleep(1)
results.push('onError-async-end')
},
onSettled: () => {
results.push('onSettled-promise')
return Promise.resolve('also-ignored') // Promise<string> (should be ignored)
},
})Attributes
- β Recommended
- π§ Fixable
File: docs/eslint/no-rest-destructuring.md
id: no-rest-destructuring title: Disallow object rest destructuring on query results
Use object rest destructuring on query results automatically subscribes to every field of the query result, which may cause unnecessary re-renders.
This makes sure that you only subscribe to the fields that you actually need.
Rule Details
Examples of incorrect code for this rule:
/* eslint "@tanstack/query/no-rest-destructuring": "warn" */
const useTodos = () => {
const { data: todos, ...rest } = useQuery({
queryKey: ['todos'],
queryFn: () => api.getTodos(),
})
return { todos, ...rest }
}Examples of correct code for this rule:
const todosQuery = useQuery({
queryKey: ['todos'],
queryFn: () => api.getTodos(),
})
// normal object destructuring is fine
const { data: todos } = todosQueryWhen typed linting is enabled, the rule also flags rest destructuring on custom hooks that return a TanStack Query result.
When Not To Use It
If you set the notifyOnChangeProps options manually, you can disable this rule.
Since you are not using tracked queries, you are responsible for specifying which props should trigger a re-render.
Attributes
- β Recommended
- π§ Fixable
File: docs/eslint/no-unstable-deps.md
id: no-unstable-deps title: Disallow putting the result of query hooks directly in a React hook dependency array
The object returned from the following query hooks is not referentially stable:
useQueryuseSuspenseQueryuseQueriesuseSuspenseQueriesuseInfiniteQueryuseSuspenseInfiniteQueryuseMutation
The object returned from those hooks should not be put directly into the dependency array of a React hook (e.g. useEffect, useMemo, useCallback).
Instead, destructure the return value of the query hook and pass the destructured values into the dependency array of the React hook.
Rule Details
Examples of incorrect code for this rule:
/* eslint "@tanstack/query/no-unstable-deps": "warn" */
import { useCallback } from 'react'
import { useMutation } from '@tanstack/react-query'
function Component() {
const mutation = useMutation({ mutationFn: (value: string) => value })
const callback = useCallback(() => {
mutation.mutate('hello')
}, [mutation])
return null
}Examples of correct code for this rule:
/* eslint "@tanstack/query/no-unstable-deps": "warn" */
import { useCallback } from 'react'
import { useMutation } from '@tanstack/react-query'
function Component() {
const { mutate } = useMutation({ mutationFn: (value: string) => value })
const callback = useCallback(() => {
mutate('hello')
}, [mutate])
return null
}Attributes
- β Recommended
- π§ Fixable
File: docs/eslint/no-void-query-fn.md
id: no-void-query-fn title: Disallow returning void from query functions
Query functions must return a value that will be cached by TanStack Query. Functions that don't return a value (void functions) can lead to unexpected behavior and might indicate a mistake in the implementation.
Rule Details
Example of incorrect code for this rule:
/* eslint "@tanstack/query/no-void-query-fn": "error" */
useQuery({
queryKey: ['todos'],
queryFn: async () => {
await api.todos.fetch() // Function doesn't return the fetched data
},
})Example of correct code for this rule:
/* eslint "@tanstack/query/no-void-query-fn": "error" */
useQuery({
queryKey: ['todos'],
queryFn: async () => {
const todos = await api.todos.fetch()
return todos
},
})Attributes
- β Recommended
- π§ Fixable
File: docs/eslint/stable-query-client.md
id: stable-query-client title: Stable Query Client
The QueryClient contains the QueryCache, so you'd only want to create one instance of the QueryClient for the lifecycle of your application - not a new instance on every render.
Exception: It's allowed to create a new QueryClient inside an async Server Component, because the async function is only called once on the server.
Rule Details
Examples of incorrect code for this rule:
/* eslint "@tanstack/query/stable-query-client": "error" */
function App() {
const queryClient = new QueryClient()
return (
<QueryClientProvider client={queryClient}>
<Home />
</QueryClientProvider>
)
}Examples of correct code for this rule:
function App() {
const [queryClient] = useState(() => new QueryClient())
return (
<QueryClientProvider client={queryClient}>
<Home />
</QueryClientProvider>
)
}const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<Home />
</QueryClientProvider>
)
}async function App() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery(options)
}Attributes
- β Recommended
- π§ Fixable
File: docs/framework/vue/guides/background-fetching-indicators.md
id: background-fetching-indicators title: Background Fetching Indicators ref: docs/framework/react/guides/background-fetching-indicators.md
<script setup>
import { useQuery } from '@tanstack/vue-query'
const { isPending, isFetching, isError, data, error } = useQuery({
queryKey: ['todos'],
queryFn: getTodos,
})
</script>
<template>
Refreshing...
Loading...
Error: {{ error.message }}
<li v-for="todo in data" :key="todo.id">{{ todo.title }}</li>
</template><script setup>
import { useIsFetching } from '@tanstack/vue-query'
const isFetching = useIsFetching()
</script>
<template>
Queries are fetching in the background...
</template>