> ## 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

> Complete guide to fetching and managing data with TanStack Query

Queries are the foundation of TanStack Query. They represent a declarative dependency on an asynchronous source of data that is tied to a unique key.

## Basic Query

To subscribe to a query in your components, use the `useQuery` hook with at least:

* A unique key for the query
* A function that returns a promise

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

function App() {
  const { data, error, status } = useQuery({
    queryKey: ['todos'],
    queryFn: async () => {
      const response = await fetch('https://api.example.com/todos')
      return response.json()
    }
  })

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

  return (
    <ul>
      {data.map(todo => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}
```

## Query Results

The query result contains all information about the query state:

```tsx theme={null}
const {
  // Data states
  data,              // The last successfully resolved data
  error,             // The error object if the query failed
  status,            // 'pending' | 'error' | 'success'
  
  // Fetch states  
  fetchStatus,       // 'fetching' | 'paused' | 'idle'
  isFetching,        // Is the query currently fetching?
  isPending,         // Is the query in pending state?
  isLoading,         // Is it both pending AND fetching?
  
  // Derived states
  isSuccess,         // Did the query succeed?
  isError,           // Did the query fail?
  isStale,           // Is the data stale?
  isPlaceholderData, // Is the data from placeholder?
  
  // Actions
  refetch,           // Function to manually refetch
  
  // Metadata
  dataUpdatedAt,     // Timestamp of last successful fetch
  errorUpdatedAt,    // Timestamp of last error
  failureCount,      // Number of consecutive failures
  failureReason,     // The error from the last failed attempt
} = useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
```

<Note>
  The `status` field indicates the data state (pending/error/success), while `fetchStatus` indicates the fetch operation state (fetching/paused/idle).
</Note>

## Query Options

### Stale Time

The time in milliseconds after data is considered stale. Defaults to `0`.

```tsx theme={null}
useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  staleTime: 5000, // Data is fresh for 5 seconds
})
```

<Tip>
  Set `staleTime: Infinity` for data that never needs to be refetched, or use the special `'static'` value for compile-time static data.
</Tip>

### GC Time (formerly Cache Time)

The time in milliseconds that unused/inactive cache data remains in memory. Defaults to 5 minutes.

```tsx theme={null}
useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  gcTime: 1000 * 60 * 10, // 10 minutes
})
```

### Retry

Control how queries retry on failure:

```tsx theme={null}
// Retry 3 times
useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  retry: 3,
})

// Retry infinitely
useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  retry: true,
})

// Conditional retry
useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  retry: (failureCount, error) => {
    // Don't retry on 404
    if (error.status === 404) return false
    // Retry up to 3 times
    return failureCount < 3
  },
})
```

### Retry Delay

Customize the delay between retries:

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

// Default: exponential backoff (1s, 2s, 4s, ...)
useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
})

// Fixed delay
useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  retryDelay: 1000, // Always wait 1 second
})
```

## Initial Data

Provide initial data to prevent loading states:

```tsx theme={null}
useQuery({
  queryKey: ['todo', todoId],
  queryFn: () => fetchTodo(todoId),
  initialData: () => {
    // Use data from another query as initial data
    return queryClient
      .getQueryData(['todos'])
      ?.find(todo => todo.id === todoId)
  },
})
```

<Note>
  Queries with `initialData` start in `success` status immediately.
</Note>

## Placeholder Data

Show temporary data while the query loads:

```tsx theme={null}
useQuery({
  queryKey: ['todo', todoId],
  queryFn: () => fetchTodo(todoId),
  placeholderData: previousData => previousData,
})
```

<Warning>
  `placeholderData` is never persisted to the cache. For persisted initial data, use `initialData`.
</Warning>

## Enabled Queries

Control when queries run:

```tsx theme={null}
function Todos({ userId }) {
  // This query will not execute until userId exists
  const { data } = useQuery({
    queryKey: ['todos', userId],
    queryFn: () => fetchTodosByUser(userId),
    enabled: !!userId,
  })
}
```

## Refetching

<Tabs>
  <Tab title="Manual Refetch">
    ```tsx theme={null}
    function Todos() {
      const { data, refetch } = useQuery({
        queryKey: ['todos'],
        queryFn: fetchTodos,
      })

      return (
        <div>
          <button onClick={() => refetch()}>Refetch</button>
          {/* ... */}
        </div>
      )
    }
    ```
  </Tab>

  <Tab title="On Window Focus">
    ```tsx theme={null}
    useQuery({
      queryKey: ['todos'],
      queryFn: fetchTodos,
      refetchOnWindowFocus: true, // Default
    })
    ```
  </Tab>

  <Tab title="On Reconnect">
    ```tsx theme={null}
    useQuery({
      queryKey: ['todos'],
      queryFn: fetchTodos,
      refetchOnReconnect: true, // Default
    })
    ```
  </Tab>

  <Tab title="On Mount">
    ```tsx theme={null}
    useQuery({
      queryKey: ['todos'],
      queryFn: fetchTodos,
      refetchOnMount: true, // Default
    })
    ```
  </Tab>
</Tabs>

## Polling with Refetch Interval

Automatically refetch at regular intervals:

```tsx theme={null}
// Poll every 5 seconds
useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  refetchInterval: 5000,
})

// Poll only when window is focused
useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  refetchInterval: 5000,
  refetchIntervalInBackground: false, // Default
})

// Dynamic interval
useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  refetchInterval: (query) => {
    return query.state.data?.length > 0 ? 10000 : 5000
  },
})
```

## Select Data

Transform or select a portion of query data:

```tsx theme={null}
useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  select: (data) => {
    // Only return completed todos
    return data.filter(todo => todo.completed)
  },
})
```

<Tip>
  The `select` function is only called when `data` exists and is memoized, so it only runs when the data changes.
</Tip>

## Dependent Queries

Queries that depend on previous queries:

```tsx theme={null}
function User({ userId }) {
  const { data: user } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
  })

  const userProjectsEnabled = !!user?.id

  const { data: projects } = useQuery({
    queryKey: ['projects', user?.id],
    queryFn: () => fetchProjectsByUser(user.id),
    enabled: userProjectsEnabled,
  })
}
```

## Parallel Queries

Multiple queries in the same component run in parallel:

```tsx theme={null}
function Overview() {
  // These run in parallel
  const usersQuery = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  })

  const projectsQuery = useQuery({
    queryKey: ['projects'],
    queryFn: fetchProjects,
  })

  const tasksQuery = useQuery({
    queryKey: ['tasks'],
    queryFn: fetchTasks,
  })
}
```

## Dynamic Parallel Queries with useQueries

For a variable number of queries:

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

function Projects({ projectIds }) {
  const projectQueries = useQueries({
    queries: projectIds.map(id => ({
      queryKey: ['project', id],
      queryFn: () => fetchProject(id),
    })),
  })

  // Check if all queries are successful
  const allSuccess = projectQueries.every(q => q.isSuccess)
}
```

## Query Cancellation

Queries support cancellation via AbortSignal:

```tsx theme={null}
useQuery({
  queryKey: ['todos'],
  queryFn: async ({ signal }) => {
    const response = await fetch('/api/todos', { signal })
    return response.json()
  },
})
```

<Note>
  When a query is cancelled (e.g., component unmounts), TanStack Query automatically calls `abort()` on the signal.
</Note>

## Error Handling

<Tabs>
  <Tab title="Per Query">
    ```tsx theme={null}
    useQuery({
      queryKey: ['todos'],
      queryFn: fetchTodos,
      throwOnError: true, // Throw errors to error boundary
    })
    ```
  </Tab>

  <Tab title="Global Default">
    ```tsx theme={null}
    const queryClient = new QueryClient({
      defaultOptions: {
        queries: {
          throwOnError: true,
        },
      },
    })
    ```
  </Tab>

  <Tab title="With Error Boundary">
    ```tsx theme={null}
    import { ErrorBoundary } from 'react-error-boundary'

    function App() {
      return (
        <ErrorBoundary fallback={<div>Something went wrong</div>}>
          <Todos />
        </ErrorBoundary>
      )
    }

    function Todos() {
      const { data } = useQuery({
        queryKey: ['todos'],
        queryFn: fetchTodos,
        throwOnError: true,
      })
      return <div>{/* ... */}</div>
    }
    ```
  </Tab>
</Tabs>

## Query Meta

Attach metadata to queries:

```tsx theme={null}
useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  meta: {
    errorMessage: 'Failed to fetch todos',
  },
})
```

Access meta in global callbacks:

```tsx theme={null}
const queryClient = new QueryClient({
  queryCache: new QueryCache({
    onError: (error, query) => {
      console.log(query.meta?.errorMessage)
    },
  }),
})
```
