query Documentation

Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.

50,118 stars TypeScript TypeScript 1 file Β· ~4,097 tokens #async#cache#data#fetch#graphql#hooks#query#react
RAW Doc

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

Partners

Explore the TanStack Ecosystem

… 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:

bash
npm i -D @tanstack/eslint-plugin-query

or

bash
pnpm add -D @tanstack/eslint-plugin-query

or

bash
yarn add -D @tanstack/eslint-plugin-query

or

bash
bun add -D @tanstack/eslint-plugin-query

Flat Config (`eslint.config.js`)

To enable all of the recommended rules for our plugin, add the following config:

js
import pluginQuery from '@tanstack/eslint-plugin-query'

export default [
  ...pluginQuery.configs['flat/recommended'],
  // Any other config...
]

The flat/recommended-strict config extends flat/recommended with additional opinionated rules that enforce best practices more aggressively.

js
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:

js
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`)

To enable all of the recommended rules for our plugin, add plugin:@tanstack/query/recommended in extends:

json
{
  "extends": ["plugin:@tanstack/query/recommended"]
}

The recommendedStrict config extends recommended with additional opinionated rules:

json
{
  "extends": ["plugin:@tanstack/query/recommendedStrict"]
}

Custom setup

Alternatively, add @tanstack/query to the plugins section, and configure the rules you want to use:

json
{
  "plugins": ["@tanstack/query"],
  "rules": {
    "@tanstack/query/exhaustive-deps": "error"
  }
}

Rules


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:

tsx
/* 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:

tsx
const Component = ({ todoId }) => {
  const todos = useTodos()
  useQuery({
    queryKey: ['todo', todoId],
    queryFn: () => todos.getTodo(todoId),
  })
}
tsx
const todos = createTodos()
const todoQueries = {
  detail: (id) => ({
    queryKey: ['todo', id],
    queryFn: () => todos.getTodo(id),
  }),
}
tsx
// 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 dependencies
  • allowlist.types: An array of TypeScript type names that should be ignored when checking dependencies
json
{
  "@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:

  • useInfiniteQuery
  • useSuspenseInfiniteQuery
  • infiniteQueryOptions

The correct property order is as follows:

  • queryFn
  • getPreviousPageParam
  • getNextPageParam

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:

tsx
/* 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:

tsx
/* 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:

  • onMutate
  • onError
  • onSettled

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:

tsx
/* 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:

tsx
/* 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:

tsx
/* 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:

tsx
const todosQuery = useQuery({
  queryKey: ['todos'],
  queryFn: () => api.getTodos(),
})

// normal object destructuring is fine
const { data: todos } = todosQuery

When 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:

  • useQuery
  • useSuspenseQuery
  • useQueries
  • useSuspenseQueries
  • useInfiniteQuery
  • useSuspenseInfiniteQuery
  • useMutation

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:

tsx
/* 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:

tsx
/* 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:

tsx
/* 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:

tsx
/* 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:

tsx
/* 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:

tsx
function App() {
  const [queryClient] = useState(() => new QueryClient())
  return (
    <QueryClientProvider client={queryClient}>
      <Home />
    </QueryClientProvider>
  )
}
tsx
const queryClient = new QueryClient()
function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Home />
    </QueryClientProvider>
  )
}
tsx
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

vue
<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>
vue
<script setup>
import { useIsFetching } from '@tanstack/vue-query'

const isFetching = useIsFetching()
</script>

<template>
  Queries are fetching in the background...
</template>