Skip to content

React Tutorial Data and APIs

Data-Fetching Libraries for React: TanStack Query and SWR

Lesson 102 of 156 3 min read By Deepak Paswan Updated Oct 10, 2026

Concept lesson · explains an idea. Code is shown for reading, not run.

Fetching data inside effects works, and you learned it so you understand what is happening. But look at what a real app needs on top of the basics:

  • Caching: do not fetch the same user three times because three components need it.
  • Deduplication: if two components ask at the same moment, send one request.
  • Refetching: update stale data when the window regains focus or the network returns.
  • Retries with sensible delays after failures.
  • Pagination and infinite lists with cached pages.
  • Optimistic updates and invalidating data after a change.
  • Status tracking (loading, error, success) with the same shape everywhere.

Writing all that in each component, or in your own hook, is slow and error-prone. Libraries have solved it.

OptionWhat it is
TanStack Query (formerly React Query)A full “server state” library: caching, refetching, mutations, pagination, devtools. Very widely used.
SWRA smaller library from Vercel with the “stale-while-revalidate” idea: show cached data, then refresh it.
Router data loaders (React Router, TanStack Router)The route loads the data before it renders; good for page-level data.
Framework data features (Next.js and others)Fetch on the server and send HTML and data to the browser; see Client-Side React vs React Frameworks.
use with SuspenseReact 19 lets a component read a promise with use, showing a Suspense fallback while it loads. You must create and cache the promise outside the render (a library or framework normally does this).
RTK Query, Apollo Client, urqlOptions when you already use Redux or GraphQL

What using one looks like

This is the shape of TanStack Query (shown for orientation; it needs npm install @tanstack/react-query and a provider at the app’s root, which is why it does not run in the playground):

App.jsx
JSX
import { useQuery } from '@tanstack/react-query'

function Users() {
  const { data, isPending, error } = useQuery({
    queryKey: ['users'],
    queryFn: () => fetch('/api/users').then((r) => r.json()),
  })

  if (isPending) return <p>Loading...</p>
  if (error) return <p>Could not load users</p>
  return <ul>{data.map((u) => <li key={u.id}>{u.name}</li>)}</ul>
}

Compare it with what you wrote by hand in Fetching Data with Effects. The library gives you the status, data and error, and in addition caches by queryKey, avoids duplicate calls and refetches in the background. Check each library’s current documentation for exact API names, because they change between major versions.

When to stay with plain fetch

  • A small app with one or two requests.
  • A single component that loads once and never refreshes.
  • Learning, and demos like the ones in this course.

When to adopt a library

  • The same data appears on several screens.
  • You need background refresh, retries or pagination.
  • You find yourself rebuilding caches and loading flags.
  • Several developers need one consistent way to handle server data.

Server state vs client state

A helpful idea: data that lives on the server and is only borrowed by the browser (users, products, orders) is server state. It can go stale, be changed by others and needs caching. Data that exists only in the browser (is the menu open, what is typed in a field) is client state. Use a data library for server state and useState or useReducer for client state. Mixing them in one big global store is a common source of complexity.

Key points

  • Libraries add caching, deduplication, refetching and retries on top of fetch.
  • Popular choices are TanStack Query, SWR and router or framework data loaders.
  • Use plain fetch for small cases; adopt a library when server data is shared or needs refreshing.