> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/TanStack/query/llms.txt
> Use this file to discover all available pages before exploring further.

# Queries

> Understanding queries and how they work in TanStack Query

Queries are the foundation of TanStack Query. A query is a declarative dependency on an asynchronous source of data that is tied to a unique key. Queries are used to fetch, cache, and update data from your server.

## Query Basics

A query requires two things:

1. **A unique query key** - An array-based identifier for the query
2. **A query function** - A function that returns a Promise that resolves data or throws an error

```tsx theme={null}
import { useQuery } from '@tanstack/react-query'

function usePosts() {
  return useQuery({
    queryKey: ['posts'],
    queryFn: async () => {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts')
      return await response.json()
    },
  })
}
```

## Query States

A query can be in one of the following states at any given moment:

### Status States

Queries have a `status` field that indicates the current state:

* **`pending`** - The query has no data yet and is currently fetching
* **`error`** - The query encountered an error
* **`success`** - The query was successful and data is available

```tsx theme={null}
function Posts() {
  const { status, data, error } = useQuery({
    queryKey: ['posts'],
    queryFn: fetchPosts,
  })

  if (status === 'pending') {
    return <div>Loading...</div>
  }

  if (status === 'error') {
    return <div>Error: {error.message}</div>
  }

  return (
    <div>
      {data.map((post) => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  )
}
```

### Fetch Status

In addition to the `status` field, queries also have a `fetchStatus` that provides additional granularity:

* **`fetching`** - The query is currently fetching
* **`paused`** - The query wanted to fetch, but it is paused (usually due to being offline)
* **`idle`** - The query is not doing anything at the moment

<Note>
  The `fetchStatus` is independent of the `status`. A query can be in `success` status but still have `fetching` fetchStatus when performing a background refetch.
</Note>

## Derived Boolean States

For convenience, the query result also includes derived boolean flags:

* **`isPending`** - Equivalent to `status === 'pending'`
* **`isSuccess`** - Equivalent to `status === 'success'`
* **`isError`** - Equivalent to `status === 'error'`
* **`isFetching`** - Equivalent to `fetchStatus === 'fetching'`
* **`isPaused`** - Equivalent to `fetchStatus === 'paused'`
* **`isLoading`** - Equivalent to `isPending && isFetching` (first load with no data)
* **`isRefetching`** - Equivalent to `isFetching && !isPending` (background refetch)

These flags are derived from the observer's state computation in `queryObserver.ts:560-589`:

```typescript theme={null}
const result: QueryObserverBaseResult<TData, TError> = {
  status,
  fetchStatus: newState.fetchStatus,
  isPending,
  isSuccess: status === 'success',
  isError,
  isInitialLoading: isLoading,
  isLoading,
  data,
  dataUpdatedAt: newState.dataUpdatedAt,
  error,
  errorUpdatedAt,
  failureCount: newState.fetchFailureCount,
  failureReason: newState.fetchFailureReason,
  errorUpdateCount: newState.errorUpdateCount,
  isFetched: newState.dataUpdateCount > 0 || newState.errorUpdateCount > 0,
  isFetchedAfterMount:
    newState.dataUpdateCount > queryInitialState.dataUpdateCount ||
    newState.errorUpdateCount > queryInitialState.errorUpdateCount,
  isFetching,
  isRefetching: isFetching && !isPending,
  isLoadingError: isError && !hasData,
  isPaused: newState.fetchStatus === 'paused',
  isPlaceholderData,
  isRefetchError: isError && hasData,
  isStale: isStale(query, options),
  refetch: this.refetch,
}
```

## Query Lifecycle

### 1. Query Observer Creation

When you use `useQuery`, a `QueryObserver` is created that subscribes to changes in the query's state. From `queryObserver.ts:71-89`:

```typescript theme={null}
constructor(
  client: QueryClient,
  public options: QueryObserverOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>,
) {
  super()
  this.#client = client
  this.#selectError = null
  this.#currentThenable = pendingThenable()
  this.bindMethods()
  this.setOptions(options)
}
```

### 2. Subscription

When the observer gets its first subscriber, it checks if it should fetch on mount (`queryObserver.ts:95-107`):

```typescript theme={null}
protected onSubscribe(): void {
  if (this.listeners.size === 1) {
    this.#currentQuery.addObserver(this)

    if (shouldFetchOnMount(this.#currentQuery, this.options)) {
      this.#executeFetch()
    } else {
      this.updateResult()
    }

    this.#updateTimers()
  }
}
```

### 3. Data Fetching

When a fetch is executed, the query transitions through states. The fetch state is defined in `query.ts:690-709`:

```typescript theme={null}
export function fetchState<TQueryFnData, TError, TData, TQueryKey extends QueryKey>(
  data: TData | undefined,
  options: QueryOptions<TQueryFnData, TError, TData, TQueryKey>,
) {
  return {
    fetchFailureCount: 0,
    fetchFailureReason: null,
    fetchStatus: canFetch(options.networkMode) ? 'fetching' : 'paused',
    ...(data === undefined &&
      ({
        error: null,
        status: 'pending',
      } as const)),
  } as const
}
```

### 4. Result Updates

As the query state changes, the observer computes new results and notifies listeners only when tracked properties change (`queryObserver.ts:641-697`).

## Stale Queries

A query is considered "stale" when it's old enough that it should be refetched. The staleness is determined by:

* The `staleTime` option (defaults to `0`, meaning data is immediately stale)
* Whether the query has been invalidated

From `query.ts:308-323`:

```typescript theme={null}
isStaleByTime(staleTime: StaleTime = 0): boolean {
  // no data is always stale
  if (this.state.data === undefined) {
    return true
  }
  // static is never stale
  if (staleTime === 'static') {
    return false
  }
  // if the query is invalidated, it is stale
  if (this.state.isInvalidated) {
    return true
  }

  return !timeUntilStale(this.state.dataUpdatedAt, staleTime)
}
```

<Tip>
  Set `staleTime` to control how long data is considered fresh. For data that doesn't change often, use a higher `staleTime` to reduce unnecessary refetches.
</Tip>

## Background Refetching

Queries automatically refetch in the background under several conditions:

* **New instances** of the query mount
* **Window is refocused** (controlled by `refetchOnWindowFocus`)
* **Network is reconnected** (controlled by `refetchOnReconnect`)
* **Refetch interval** is configured (via `refetchInterval`)

```tsx theme={null}
const { data } = useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  staleTime: 5000, // Data is fresh for 5 seconds
  refetchOnWindowFocus: true, // Refetch when window regains focus
  refetchOnReconnect: true, // Refetch when network reconnects
  refetchInterval: 30000, // Refetch every 30 seconds
})
```

## Enabled Queries

Queries can be disabled using the `enabled` option. This is useful for dependent queries:

```tsx theme={null}
function usePost(postId: number) {
  return useQuery({
    queryKey: ['post', postId],
    queryFn: () => fetchPost(postId),
    enabled: !!postId, // Only run when postId is truthy
  })
}
```

## Initial Data

You can provide initial data for a query, which will be used immediately while the query fetches in the background:

```tsx theme={null}
const { data } = useQuery({
  queryKey: ['post', postId],
  queryFn: () => fetchPost(postId),
  initialData: () => {
    // Use data from another query as initial data
    return queryClient
      .getQueryData(['posts'])
      ?.find((post) => post.id === postId)
  },
})
```

## Placeholder Data

Placeholder data allows you to show fake data while the real data is loading. Unlike `initialData`, placeholder data is not persisted to the cache:

```tsx theme={null}
const { data } = useQuery({
  queryKey: ['post', postId],
  queryFn: () => fetchPost(postId),
  placeholderData: (previousData, previousQuery) => {
    // Show previous data while fetching new data
    return previousData
  },
})
```

<Warning>
  Placeholder data is **not** cached and will be replaced by actual data when it arrives. Use `initialData` if you want the data to be persisted to the cache.
</Warning>

## Query Function Context

Query functions receive a `QueryFunctionContext` object with useful properties:

* **`queryKey`** - The query key
* **`signal`** - An AbortSignal for cancellation
* **`meta`** - Optional metadata
* **`pageParam`** - For infinite queries

From `types.ts:138-165`, the context is defined as:

```typescript theme={null}
export type QueryFunctionContext<
  TQueryKey extends QueryKey = QueryKey,
  TPageParam = never,
> = {
  client: QueryClient
  queryKey: TQueryKey
  signal: AbortSignal
  meta: QueryMeta | undefined
  pageParam?: unknown
  direction?: unknown
}
```

## Type Safety

TanStack Query provides full type safety for your queries:

```tsx theme={null}
type Post = {
  id: number
  title: string
  body: string
}

function usePosts() {
  return useQuery<Post[], Error>({
    queryKey: ['posts'],
    queryFn: async (): Promise<Post[]> => {
      const response = await fetch('https://api.example.com/posts')
      return await response.json()
    },
  })
}

function Posts() {
  const { data } = usePosts()
  // data is typed as Post[] | undefined
}
```

## Error Handling

When a query function throws an error, the query enters the `error` state:

```tsx theme={null}
function usePosts() {
  return useQuery({
    queryKey: ['posts'],
    queryFn: async () => {
      const response = await fetch('https://api.example.com/posts')
      if (!response.ok) {
        throw new Error('Failed to fetch posts')
      }
      return await response.json()
    },
    retry: 3, // Retry failed requests 3 times
    retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
  })
}
```

<Note>
  By default, TanStack Query will retry failed queries 3 times with exponential backoff before setting the query to an `error` state.
</Note>
