How to Use TanStack Query: The Beginner's Guide (First Query, Caching Rules, Mutations, and the Devtools Habit)
CodexFuse Team · 2026-10-31T03:57:27.257Z · 11 min read
Learn how to use TanStack Query v5 in React: install the package, write your first useQuery, set staleTime and gcTime caching rules, handle mutations with invalidation, and read the Devtools like a debugger.
How to Use TanStack Query: The Beginner's Guide (First Query, Caching Rules, Mutations, and the Devtools Habit)
Learning how to use TanStack Query is the fastest way to delete a hundred lines of data-fetching boilerplate from a React app — the loading flags, the error states, the tangled useEffect chains, the manual refetch buttons. TanStack Query, the library most developers still call React Query, is the standard way to manage server state in React: it fetches your data, caches it, keeps it fresh in the background, and updates it after writes. This TanStack Query tutorial for beginners follows the arc the official docs prescribe, using the current v5 API: the server-state mental model, install, your first useQuery, the staleTime and gcTime caching rules, mutations with invalidation, and the Devtools habit.
One practical note before the code: every example below calls a JSON API. To shape the requests your first queries will make, try our free REST API tester — it builds the exact GET calls your query functions will wrap. And if you want a real backend to practice against, our Supabase beginner guide walks you through a free Postgres API you can query in about an hour. Our VS Code beginner guide covers the editor setup if you need it.
What is TanStack Query, exactly?
TanStack Query is a React data-fetching library for server state — data that lives on a server and arrives asynchronously. That one sentence is the whole mental model, and the official docs open with it: TanStack Query "makes fetching, caching, synchronizing and updating server state a breeze," explicitly contrasting server state with client state, which traditional state libraries handle (tanstack.com/query/latest/docs, fetched October 30, 2026). The landing page calls it "the server-state standard for modern frontend apps" (tanstack.com/query/latest, fetched October 30, 2026).
Draw the line between the two kinds of state before you write any code. Client state is stuff your app owns: the theme toggle, the open modal, the form draft. Server state is stuff you borrow: the todo list, the user profile, the search results — it can change on the server without your app knowing. TanStack Query owns the borrowing. It is not a replacement for Redux or Zustand, which own the owning.
One naming note: TanStack Query is the current name of React Query. The library was renamed as it grew framework adapters beyond React, but the React package is still @tanstack/react-query and most tutorials still say "React Query" — this TanStack Query tutorial covers the same library (official quick start, tanstack.com/query/latest/docs/framework/react/quick-start, fetched October 30, 2026).
How to use TanStack Query for the first time?
The whole beginner arc is seven steps, in this order — each section of this guide covers one:
- Install the package:
npm install @tanstack/react-query - Create a QueryClient and wrap your app in QueryClientProvider
- Write your first useQuery with a query key array and a query function
- Handle the isPending and isError states
- Set your staleTime and gcTime caching rules
- Handle writes with useMutation plus invalidateQueries
- Open the Devtools and watch your cache like a debugger
How do you install TanStack Query in a React app?
You install it with one npm command, and it requires React 18 or newer. The official installation page names npm as the install route and lists React 18+ as the requirement, working with both ReactDOM and React Native (tanstack.com/query/latest/docs/framework/react/installation, fetched October 30, 2026). The package name is @tanstack/react-query (flaviocopes.com/tanstack-query, updated September 2026):
npm install @tanstack/react-query
Then create a QueryClient and provide it to your app. This exact snippet from the official v5 quick start is the entire setup — nothing else is needed before your first query works:
import {
useQuery,
useMutation,
useQueryClient,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<Todos />
</QueryClientProvider>
)
}
Snippet: official quick start (tanstack.com/query/latest/docs/framework/react/quick-start, fetched October 30, 2026). Forgetting the provider is the most common beginner error — without it, every useQuery throws a "No QueryClient set" runtime error, so wrap the app once at the root and forget about it.
What does your first useQuery look like?
Your first useQuery takes two things: a query key (always an array) that uniquely identifies the data, and a query function that returns a promise. TanStack Query calls the function for you, caches the result under the key, and hands back the state:
function Todos() {
const { data, isPending, isError, error } = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/api/todos').then(res => {
if (!res.ok) throw new Error('Network response was not ok')
return res.json()
}),
})
if (isPending) return <span>Loading...</span>
if (isError) return <span>Error: {error.message}</span>
return (
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
)
}
Three things to notice. First, the query key is an array — always. Keys with parameters live in the same array, like ['todos', userId], and each variation gets its own cache entry. Second, you never call queryFn yourself; TanStack Query decides when to run it based on your caching rules. Third, the state flag is isPending, not isLoading — v5 renamed the flag, and isLoading survives only as a legacy alias (official useQuery reference, tanstack.com/query/latest/docs/framework/react/reference/useQuery, fetched October 30, 2026). Most older beginner guides still teach isLoading and cacheTime because they were written for v4; if a tutorial shows either, it is the old version.
How do staleTime and gcTime control TanStack Query caching?
Two numbers run TanStack Query's caching, and confusing them is the classic beginner bug. staleTime is how long fetched data counts as fresh — the default is 0, meaning data goes stale immediately and TanStack Query refetches it on the next mount or window focus. gcTime (garbage-collection time) is how long unused cache data stays in memory after nothing uses it — the default is 5 minutes (official useQuery options table, fetched October 30, 2026). Raising staleTime stops background refetches; raising gcTime keeps data around longer when you navigate away and back.
Use this decision table as your starting point:
| Data type | staleTime | Why |
|---|---|---|
| App config, rarely-changing lists | High (minutes to hours) | No point refetching what never changes |
| User profile | A few minutes | Changes rarely, but staleness is noticeable |
| Dashboard stats, prices | 0 (the default) | You want the freshest number every time |
| Search results | 0, with keys like ['search', query] | Each key caches its own query's results |
The other defaults worth memorizing from the same options table: retries are 3 on the client and 0 on the server, refetchOnWindowFocus and refetchOnMount and refetchOnReconnect are all true, enabled is true, refetchInterval is false, and structuralSharing is true — meaning new data that is deeply equal to old data will not trigger a re-render (official useQuery reference, fetched October 30, 2026).
How do you handle mutations and keep the cache fresh?
You write with useMutation, and you keep the cache honest by invalidating the query keys your write touched. This official v5 pattern from the quick start pairs the mutation with an onSuccess invalidation:
function AddTodo() {
const queryClient = useQueryClient()
const mutation = useMutation({
mutationFn: (newTodo) =>
fetch('/api/todos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newTodo),
}).then(res => res.json()),
onSuccess: () => {
// Mark ['todos'] stale so the list refetches the new item
queryClient.invalidateQueries({ queryKey: ['todos'] })
},
})
return (
<button
onClick={() => mutation.mutate({ title: 'Learn TanStack Query' })}
disabled={mutation.isPending}
>
{mutation.isPending ? 'Adding...' : 'Add todo'}
</button>
)
}
Invalidation is the beginner-safe default: mark the cache entry stale and let the library refetch it. Optimistic updates — painting the new item in the UI before the server answers — are the advanced version. Learn invalidation first; it is one line and can never show wrong data.
If your writes go to a real backend, the free Postgres API from the Supabase guide builds tables that make perfect mutation practice — create rows, invalidate, and watch the list refresh.
What changed between v4 and v5?
If you learned React Query from an older guide, v5 renamed the two things beginners touch most. Check the table before trusting any tutorial dated before 2024:
| v4 name | v5 name | What to know |
|---|---|---|
| isLoading | isPending | isLoading still exists as a legacy alias (isFetching && isPending); write isPending |
| cacheTime | gcTime | Same job (how long unused cache lives), new name |
| isInitialLoading | Deprecated | Removed from the v5 API surface |
Facts: official useQuery reference (tanstack.com/query/latest/docs/framework/react/reference/useQuery, fetched October 30, 2026); cacheTime-to-gcTime rename confirmed by dev.to/rajat128, "From Beginner to Pro: Mastering State Management with TanStack Query v5" (~295 days old). When a guide mixes isLoading with gcTime, it was updated halfway — follow the v5 column.
Why is useQuery better than fetching in useEffect?
The useEffect version of data fetching is the boilerplate TanStack Query exists to delete: a useState for the data, a useState for loading, a useState for the error, a useEffect that fetches on mount — and no cache, so mounting the component twice fetches twice. One useQuery replaces all of that and adds what the manual version never had: a shared cache, background refetching on window focus, automatic request deduplication, and retries. The "useEffect vs useQuery" framing is also how most beginner guides on page one teach it, including dev.to/tphilus's 230-day "mastering" piece (fetched October 30, 2026).
What do the TanStack Query Devtools show you?
The Devtools are a separate package — @tanstack/react-query-devtools — that adds a floating panel listing every query in your cache: its key, its state (fresh, stale, fetching), its data, and when it last updated (stacknotice.com, "TanStack Query v5 Complete Guide (2026)," updated ~May 2026). Install it as a dev dependency and mount the component inside your provider:
npm install -D @tanstack/react-query-devtools
The beginner habit: keep the panel open while you develop and watch keys move from fresh to stale to fetching. It is the fastest way to learn what staleTime and gcTime actually do, because you see the cache instead of imagining it. (Package name and install route here are from a third-party 2026 guide, not the official docs page — unverified against the official devtools docs this run.)
What mistakes do TanStack Query beginners make?
Five, and they all come from old tutorials or skipped steps:
| Mistake | Why it hurts | The fix |
|---|---|---|
| Copying isLoading / cacheTime from v4 guides | Legacy flags, stale patterns | Use isPending and gcTime — v5 only |
| String query keys like 'todos' | No parameter scoping, cache collisions | Always arrays: ['todos'], ['todos', id] |
| Forgetting QueryClientProvider | "No QueryClient set" runtime error | Wrap the app once, at the root |
| Using it for client state (theme, modals) | Network-shaped code for local UI | Keep client state in useState or Zustand |
| Mutations without invalidation | UI shows a stale list after a write | invalidateQueries on the touched key in onSuccess |
When your practice app grows past one file, push it to GitHub — our GitHub beginner guide covers the first repository, first push, and first pull request. Commits are cheap, and the push habit is the same save-point loop TanStack Query automates for your data.
Sources
- TanStack Query quick start (React) — package name, provider snippet, mutation + invalidation pattern: https://tanstack.com/query/latest/docs/framework/react/quick-start (fetched October 30, 2026)
- TanStack Query installation — npm route, React 18+ requirement, ReactDOM and React Native support: https://tanstack.com/query/latest/docs/framework/react/installation (fetched October 30, 2026; install code block did not render in the fetch)
- TanStack Query useQuery reference — isPending/isError states, gcTime, full option defaults table: https://tanstack.com/query/latest/docs/framework/react/reference/useQuery (fetched October 30, 2026)
- TanStack Query queries guide — isPending as the primary flag: https://tanstack.com/query/latest/docs/framework/react/guides/queries (fetched October 30, 2026)
- TanStack Query docs overview — server-state positioning vs client state: https://tanstack.com/query/latest/docs (fetched October 30, 2026)
- TanStack Query landing page — "the server-state standard for modern frontend apps": https://tanstack.com/query/latest (fetched October 30, 2026)
- Flavio Copes, "Introduction to TanStack Query" — install command, query-key and queryFn arc: https://flaviocopes.com/tanstack-query/ (updated September 9, 2026)
- StackNotice, "TanStack Query v5 Complete Guide (2026)" — devtools package, v5 setup arc: https://stacknotice.com/blog/tanstack-query-v5-complete-guide-2026 (updated ~May 2026)
- dev.to/rajat128, "From Beginner to Pro: Mastering State Management with TanStack Query v5" — cacheTime-to-gcTime rename, v5 defaults: https://dev.to/rajat128/from-beginner-to-pro-mastering-state-management-with-tanstack-query-v5 (~295 days old)
- MakerKit, "How to Use Supabase with TanStack Query (React Query v5)" — v5 current, v4-to-v5 change table: https://makerkit.dev/blog/tutorials/supabase-react-query (updated January 2026)
- dev.to/tphilus, "Mastering TanStack Query for Cleaner Data Fetching" — useEffect-vs-useQuery framing: https://dev.to/tphilus/mastering-tanstack-query-for-cleaner-data-fetching-2a31 (~230 days old)
Frequently asked questions
Is TanStack Query the same thing as React Query?
Yes — same library, new name. The project was renamed TanStack Query when it grew adapters beyond React, but the React package is still @tanstack/react-query and its API is what every "React Query tutorial" teaches (official quick start, tanstack.com, fetched October 30, 2026). Learning one means knowing the other.
Does TanStack Query replace Redux or Zustand?
No. TanStack Query manages server state — data that lives on a server and arrives asynchronously — while Redux and Zustand manage client state like theme toggles and open modals (tanstack.com/query/latest/docs, fetched October 30, 2026). Most real apps use both: TanStack Query for the borrowing, a client-state library for the owning.
What is the difference between staleTime and gcTime?
staleTime is how long fetched data counts as fresh (default 0 — refetch on the next mount or window focus); gcTime is how long unused cache data stays in memory (default 5 minutes) (official useQuery reference, fetched October 30, 2026). Raise staleTime to stop background refetches, raise gcTime to keep data around longer when navigating back.
Do I have to install the Devtools?
No, but you should. The Devtools are a separate package (@tanstack/react-query-devtools) that shows every cached query, its key, its state, and its data in a floating panel (stacknotice.com v5 guide, updated ~May 2026). Beginners learn caching twice as fast when they can see it.
Does TanStack Query work with React Native?
Yes. The official installation page lists React Native alongside ReactDOM as supported, with React 18 or newer required (tanstack.com/query/latest/docs/framework/react/installation, fetched October 30, 2026).