Skip to main content

Quick Start

This guide walks you through building your first application with TanStack Query. We’ll use React in this example, but the concepts apply to all supported frameworks.

Prerequisites

Before you begin, make sure you have:
  • Node.js installed (version 16 or higher recommended)
  • A React application set up (using Vite, Create React App, or Next.js)
  • TanStack Query installed (see Installation)

Setup

1

Create a QueryClient

The QueryClient manages your queries and cache. Create one at the root of your application:
import { QueryClient } from '@tanstack/react-query'

const queryClient = new QueryClient()
You can configure default options for all queries when creating the client:
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5 minutes
      gcTime: 1000 * 60 * 60 * 24, // 24 hours
    },
  },
})
2

Wrap your app with QueryClientProvider

The QueryClientProvider makes the QueryClient available to your entire component tree:
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

const queryClient = new QueryClient()

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      {/* Your app components */}
      <Example />
    </QueryClientProvider>
  )
}
3

Create your first query

Use the useQuery hook to fetch and cache data. You need:
  • A unique queryKey to identify the query
  • A queryFn that returns a promise
import { useQuery } from '@tanstack/react-query'

function Example() {
  const { data, isPending, error } = useQuery({
    queryKey: ['repoData'],
    queryFn: async () => {
      const response = await fetch(
        'https://api.github.com/repos/TanStack/query'
      )
      if (!response.ok) {
        throw new Error('Network response was not ok')
      }
      return response.json()
    },
  })

  if (isPending) return <div>Loading...</div>

  if (error) return <div>An error occurred: {error.message}</div>

  return (
    <div>
      <h1>{data.full_name}</h1>
      <p>{data.description}</p>
      <div>
        <strong>{data.stargazers_count}</strong>
        <strong> 🍴 {data.forks_count}</strong>
        <strong> 👀 {data.subscribers_count}</strong>
      </div>
    </div>
  )
}
TanStack Query automatically handles:
  • Caching the response
  • Deduplicating identical requests
  • Refetching when the window regains focus
  • Retrying failed requests
  • Updating loading and error states
4

Add DevTools (Optional)

The DevTools help you visualize and debug your queries during development:
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

const queryClient = new QueryClient()

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Example />
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  )
}
The DevTools are automatically excluded from production builds, so you can safely leave them in your code.

Complete Example

Here’s the complete working example:
import React from 'react'
import ReactDOM from 'react-dom/client'
import {
  QueryClient,
  QueryClientProvider,
  useQuery,
} from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

const queryClient = new QueryClient()

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <ReactQueryDevtools />
      <Example />
    </QueryClientProvider>
  )
}

function Example() {
  const { isPending, error, data, isFetching } = useQuery({
    queryKey: ['repoData'],
    queryFn: async () => {
      const response = await fetch(
        'https://api.github.com/repos/TanStack/query'
      )
      return response.json()
    },
  })

  if (isPending) return 'Loading...'

  if (error) return 'An error has occurred: ' + error.message

  return (
    <div>
      <h1>{data.full_name}</h1>
      <p>{data.description}</p>
      <strong>👀 {data.subscribers_count}</strong>{' '}
      <strong>{data.stargazers_count}</strong>{' '}
      <strong>🍴 {data.forks_count}</strong>
      <div>{isFetching ? 'Updating...' : ''}</div>
    </div>
  )
}

const rootElement = document.getElementById('root') as HTMLElement
ReactDOM.createRoot(rootElement).render(<App />)

Understanding the Query Result

The useQuery hook returns an object with several useful properties:
  • data - The successfully fetched data (undefined until the query succeeds)
  • error - The error object if the query failed (null if successful)
  • isPending - true if the query is currently fetching for the first time
  • isError - true if the query encountered an error
  • isSuccess - true if the query was successful
  • isFetching - true whenever the query is fetching (including background refetches)
  • refetch - A function to manually trigger a refetch

Query Keys

Query keys uniquely identify your queries. They can be:
// Simple string key
useQuery({ queryKey: ['todos'], ... })

// Array with multiple values
useQuery({ queryKey: ['todo', todoId], ... })

// Complex nested structure
useQuery({ queryKey: ['todos', { status: 'active', page: 1 }], ... })
When any value in the query key changes, TanStack Query automatically refetches the data. This makes dependent queries trivial to implement.

What Happens Automatically?

With zero configuration, TanStack Query:
  1. Caches your data in memory
  2. Deduplicates identical requests that happen simultaneously
  3. Refetches data when the window regains focus
  4. Refetches data when the network reconnects
  5. Retries failed requests with exponential backoff
  6. Marks data as stale after a configurable time period
  7. Garbage collects unused cache data

Next Steps

Now that you’ve built your first query, explore more advanced features:

Query Options

Configure caching, refetching, and retry behavior

Mutations

Learn how to create, update, and delete data

Query Invalidation

Keep your data fresh after mutations

Pagination

Implement paginated and infinite scroll queries

Framework-Specific Examples

While this guide uses React, the concepts are identical across frameworks. The main differences are:
  • Vue: Use useQuery from @tanstack/vue-query with Vue’s Composition API
  • Solid: Use createQuery from @tanstack/solid-query as a primitive
  • Svelte: Use createQuery from @tanstack/svelte-query with Svelte’s reactive syntax
  • Angular: Use injectQuery from @tanstack/angular-query-experimental with Signals
Check the framework-specific documentation for detailed examples.

Build docs developers (and LLMs) love