📄 tanstack/query/v4/docs/framework/react/guides/queries

File: queries.md | Updated: 11/15/2025

Source: https://tanstack.com/query/v4/docs/framework/react/guides/queries



TanStack

Query v4v4

Search...

+ K

Auto

Log In

TanStack StartRC

Docs Examples GitHub Contributors

TanStack Router

Docs Examples GitHub Contributors

TanStack Query

Docs Examples GitHub Contributors

TanStack Table

Docs Examples Github Contributors

TanStack Formnew

Docs Examples Github Contributors

TanStack DBbeta

Docs Github Contributors

TanStack Virtual

Docs Examples Github Contributors

TanStack Paceralpha

Docs Examples Github Contributors

TanStack Storealpha

Docs Examples Github Contributors

TanStack Devtoolsalpha

Docs Github Contributors

More Libraries

Maintainers Partners Support Learn StatsBETA Discord Merch Blog GitHub Ethos Brand Guide

Documentation

Framework

React logo

React

Version

v4

Search...

+ K

Menu

Getting Started

Guides & Concepts

Community Resources

API Reference

ESLint

Plugins

Examples

Framework

React logo

React

Version

v4

Menu

Getting Started

Guides & Concepts

Community Resources

API Reference

ESLint

Plugins

Examples

On this page

Queries

Copy Markdown

Query Basics
------------

A query is a declarative dependency on an asynchronous source of data that is tied to a unique key. A query can be used with any Promise based method (including GET and POST methods) to fetch data from a server. If your method modifies data on the server, we recommend using Mutations instead.

To subscribe to a query in your components or custom hooks, call the useQuery hook with at least:

  • A unique key for the query
  • A function that returns a promise that:
    • Resolves the data, or
    • Throws an error

tsx

import { useQuery } from '@tanstack/react-query'

function App() {
  const info = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList })
}


import { useQuery } from '@tanstack/react-query'

function App() {
  const info = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList })
}

The unique key you provide is used internally for refetching, caching, and sharing your queries throughout your application.

The query result returned by useQuery contains all of the information about the query that you'll need for templating and any other usage of the data:

tsx

const result = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList })


const result = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList })

The result object contains a few very important states you'll need to be aware of to be productive. A query can only be in one of the following states at any given moment:

  • isLoading or status === 'loading' - The query has no data yet
  • isError or status === 'error' - The query encountered an error
  • isSuccess or status === 'success' - The query was successful and data is available

Beyond those primary states, more information is available depending on the state of the query:

  • error - If the query is in an isError state, the error is available via the error property.
  • data - If the query is in an isSuccess state, the data is available via the data property.
  • isFetching - In any state, if the query is fetching at any time (including background refetching) isFetching will be true.

For most queries, it's usually sufficient to check for the isLoading state, then the isError state, then finally, assume that the data is available and render the successful state:

tsx

function Todos() {
  const { isLoading, isError, data, error } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodoList,
  })

  if (isLoading) {
    return <span>Loading...</span>
  }

  if (isError) {
    return <span>Error: {error.message}</span>
  }

  // We can assume by this point that `isSuccess === true`
  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}


function Todos() {
  const { isLoading, isError, data, error } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodoList,
  })

  if (isLoading) {
    return <span>Loading...</span>
  }

  if (isError) {
    return <span>Error: {error.message}</span>
  }

  // We can assume by this point that `isSuccess === true`
  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}

If booleans aren't your thing, you can always use the status state as well:

tsx

function Todos() {
  const { status, data, error } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodoList,
  })

  if (status === 'loading') {
    return <span>Loading...</span>
  }

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

  // also status === 'success', but "else" logic works, too
  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}


function Todos() {
  const { status, data, error } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodoList,
  })

  if (status === 'loading') {
    return <span>Loading...</span>
  }

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

  // also status === 'success', but "else" logic works, too
  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}

TypeScript will also narrow the type of data correctly if you've checked for loading and error before accessing it.

### FetchStatus

In addition to the status field and the result object, you will also get an additional fetchStatus property with the following options:

  • fetchStatus === 'fetching' - The query is currently fetching.
  • fetchStatus === 'paused' - The query wanted to fetch, but it is paused. Read more about this in the Network Mode guide.
  • fetchStatus === 'idle' - The query is not doing anything at the moment.

### Why two different states?

Background refetches and stale-while-revalidate logic make all combinations for status and fetchStatus possible. For example:

  • a query in success status will usually be in idle fetchStatus, but it could also be in fetching if a background refetch is happening.
  • a query that mounts and has no data will usually be in loading status and fetching fetchStatus, but it could also be paused if there is no network connection.

So keep in mind that a query can be in loading state without actually fetching data. As a rule of thumb:

  • The status gives information about the data: Do we have any or not?
  • The fetchStatus gives information about the queryFn: Is it running or not?

Further Reading
---------------

For an alternative way of performing status checks, have a look at the Community Resources .

Edit on GitHub

Important Defaults

Query Keys

Partners Become a Partner

Code RabbitCode Rabbit CloudflareCloudflare AG GridAG Grid NetlifyNetlify NeonNeon WorkOSWorkOS ClerkClerk ConvexConvex ElectricElectric SentrySentry PrismaPrisma StrapiStrapi UnkeyUnkey

[###### Want to Skip the Docs?

Query.gg - The Official React Query Course
\

“If you’re serious about *really* understanding React Query, there’s no better way than with query.gg”—Tanner Linsley

Learn More](https://query.gg/?s=tanstack)

You are currently reading v4 docs. Redirect to latest version?

Latest Hide

scarf analytics