MainAdvanced ExamplesRecommended Reading
TLDR /Data Fetching

React Query Cheatsheet

Help others learn from this page

TanStack Query (formerly known as React Query) is a powerful data-fetching and caching library for React. It helps manage remote state (like API calls) declaratively, with built-in caching, background sync, pagination, and more — removing the need for useEffect, local loading/error state, and boilerplate fetch logic.

Use it when:

  • You're fetching remote data (REST, GraphQL, RPC)
  • You want stale-while-revalidate caching, pagination, or background refetches
  • You want to avoid manually tracking loading, errors, retries, etc.

It complements client state managers (like Zustand or Redux), but often replaces them entirely for server state.

NOTE: Tested with the latest version at time of writing, v5.

Basic Query

Fetch server data and handle loading/error state automatically. Cached by queryKey.

const { data, error, isPending } = useQuery({
  queryKey: ['todos'],
  queryFn: () => fetch('/api/todos').then(res => res.json())
});

Mutations

Use useMutation for POST/PUT/DELETE. Manually trigger and invalidate affected queries after success.

const mutation = useMutation({
  mutationFn: (newTodo) => fetch('/api/todos', {
    method: 'POST',
    body: JSON.stringify(newTodo)
  }),
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] })
});

Invalidate & Refetch

Trigger a fresh fetch manually or after a mutation.

queryClient.invalidateQueries({ queryKey: ['todos'] }); // mark stale
queryClient.refetchQueries({ queryKey: ['todos'] });    // force refetch now

Query Options

Fine-tune behavior: when to run, how long to cache, etc.

useQuery({
  queryKey: ['user', userId],
  queryFn: () => fetchUser(userId),
  enabled: !!userId,            // don't run until userId exists
  staleTime: 1000 * 60 * 5      // 5 minutes before stale
});

Polling / Intervals

Use refetchInterval to poll at regular intervals.

useQuery({
  queryKey: ['metrics'],
  queryFn: fetchMetrics,
  refetchInterval: 5000 // every 5 seconds
});

Dependent Queries

Only run if a value exists (e.g. after login or param load).

useQuery({
  queryKey: ['account', userId],
  queryFn: () => fetchAccount(userId),
  enabled: !!userId
});

Infinite / Paginated Queries

Fetch paginated data (infinite scroll or cursor-based).

useInfiniteQuery({
  queryKey: ['feed'],
  queryFn: ({ pageParam }) => fetchFeed({ cursor: pageParam }),
  initialPageParam: null,
  getNextPageParam: (lastPage) => lastPage.nextCursor
});

Prefetching

Preload data into the cache before it's needed (e.g. on hover).

queryClient.prefetchQuery({
  queryKey: ['projects'],
  queryFn: fetchProjects
});

Cancellation (Built-In)

Queries are automatically canceled when:

  • Component unmounts
  • queryKey changes mid-fetch
  • A new fetch starts before the last finishes

Mutation Status

Track mutation progress explicitly: pending, error, success.

const { mutate, isPending, isSuccess, isError } = useMutation({
  mutationFn: saveSettings
});

QueryClient Setup

Create and provide a QueryClient at app root.

const queryClient = new QueryClient();

<QueryClientProvider client={queryClient}>
  <App />
</QueryClientProvider>

Devtools

Add optional devtools for live query inspection.

import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

<ReactQueryDevtools initialIsOpen={false} />

Best Practices

  • Always use array-style queryKey: ['posts', postId]
  • Use enabled: false to control execution timing
  • Invalidate queries after mutations to sync UI
  • Keep queries co-located near the components that use them
  • Use staleTime for frequently-read but slow-changing data
  • Prefer useMutation for any write action, even PUTs
  • Wrap all query logic in custom hooks to simplify reuse

Frequently Asked Questions

FAQ

Do I need Redux or Zustand with React Query?
Not for server data. React Query handles fetching, caching, and syncing with the backend. Keep Redux/Zustand for purely client-side state like modals or UI themes.
Is React Query REST or GraphQL?
Neither. It's transport-agnostic — works with any data source (REST, GraphQL, Firebase, Supabase, etc.).
Can I use React Query with SSR or Next.js?
Yes. React Query supports SSR with hydration. It's a great match for Next.js apps that need preloaded data.
How does React Query handle caching?
It uses normalized cache keys and tracks freshness (via staleTime and cacheTime). You can also manually invalidate or refetch data.

Related Concepts

Found this helpful? Share it!