← Back to list

TanStack Query vs SWR: Choosing the right data fetching library

They both work. Here’s why the choice still matters.

ujevicigor · 2026-04-06 12:53 · 13 claps · 5.1 min read
#react #tanstack-query #vercel #swr #javascript
Open on Medium ↗
Wiki topics: 🌐 · Web Development 📚 · Books & Reading

TanStack Query vs SWR: Choosing the right data fetching library

Both libraries address the same core problem and do it effectively. The difference lies in how much assistance you want along the way.

Every React app that communicates with a server must address the same questions. When should I fetch data? What should I display while I wait? What happens if the request fails? How long can I rely on the data I already have before checking back with the server?

For most of React’s early years, developers answered these questions by hand — a useEffect here, a loading boolean there, maybe a shared context if they were feeling organized. It worked, until it didn't. The boilerplate piled up. Edge cases multiplied. Race conditions crept in. Cache invalidation, as the saying goes, is one of the two hard problems in computer science.

**TanStack Query and [SWR](https://swr.vercel.app/)** are the two libraries that genuinely solved this. Not just patched it — solved it. They introduced a new mental model where server data has its own lifecycle: it gets fetched when needed, cached, kept fresh in the background, and garbage collected when it’s no longer relevant. They handle deduplication, retries, background refetching, optimistic updates, and pagination. They make race conditions nearly impossible to write accidentally. They give you loading and error states for free. In short, they took a category of problems that used to consume significant engineering time and made them largely invisible.

Both have become a staple of the modern React stack. The question isn’t really whether to use one — it’s which one fits where you’re going.

What they have in common

Before getting into the differences, it’s worth appreciating how much shared ground there is. These two libraries converge on most of the things that actually matter to day-to-day development.

  • Automatic caching & request deduplication
  • Background revalidation (stale-while-revalidate)
  • Refetch on window focus & reconnect
  • Polling & interval-based fetching
  • Optimistic UI & local mutations
  • Pagination & infinite scroll
  • SSR support (Next.js & beyond)
  • React Suspense compatibility
  • Full TypeScript support
  • Zero external dependencies (almost 😄)

If your requirements are modest — a handful of API endpoints, standard loading and error states, maybe some pagination — either library will serve you well. The real differences emerge when you push beyond the basics.

The five differences that actually matter

1. Philosophy: a toolkit vs a sharp instrument

This is the root of every other difference. TanStack Query approaches data fetching as a system. It ships with a dedicated DevTools panel, a rich API for cache manipulation, first-class mutation primitives, offline mode, query cancellation, and a plugin architecture. It wants to own your entire async data lifecycle.

SWR takes the opposite stance. It’s built around a single hook — useSWR — with a small, stable API surface. The name itself describes the pattern: stale-while-revalidate. Fetch, show cached data immediately, revalidate in the background. That’s the pitch, and Vercel has stayed true to it across every version.

SWR asks: what’s the minimum API needed to make data fetching correct? TanStack Query asks: what’s the maximum surface area we can make feel effortless?

Neither answer is wrong. But they lead to fundamentally different libraries, and fundamentally different experiences for the teams using them.

2. Mutations: first-class vs bring-your-own

If you build anything beyond a read-only UI, mutations are where the rubber meets the road. TanStack Query’s useMutation hook is a full state machine — it tracks pending, success, and error states, fires lifecycle callbacks (onMutate, onSuccess, onError, onSettled), and gives you a structured pattern for optimistic updates with automatic rollback if the server rejects the change. The connection between a mutation and the queries it should invalidate is explicit and handled entirely within the library.

SWR introduced useSWRMutation in v2, which covers the basics. But for more complex flows — multiple dependent invalidations, rollback on error, mutation queuing — you’re largely writing that logic yourself. That’s not necessarily a problem if you have a small surface area; it does become a problem as that surface area grows.

3. Cache control: granular vs intentional simplicity

TanStack Query gives you a rich vocabulary for cache behavior. You can set staleTime per-query (how long before data is considered stale), gcTime (how long before it’s garbage-collected from memory), invalidateQueries by key pattern, and write directly into the cache with setQueryData. These controls let you build sophisticated caching strategies — for instance, keeping user profile data fresh for 30 seconds while keeping reference data (countries, categories) for an hour.

SWR’s model is simpler: you tell it how long to consider data fresh, and you call mutate(key) to trigger revalidation. For most applications, this is sufficient. But if you’re building something where cache precision genuinely matters — a real-time dashboard, a collaborative editor, a complex multi-step form — TanStack Query’s granularity becomes hard to live without.

4. Framework support

TanStack Query runs on React, Vue, Solid, Svelte, Angular, and Preact — all from the same core package with framework-specific adapters. If you work in a monorepo with multiple frontend frameworks, or if there’s any chance your team migrates away from React, this matters.

SWR is React-only. This isn’t a criticism — it means the library can optimize hard for React’s model without compromise — but it is a real constraint if your organization’s stack is heterogeneous.

5. Key handling: strings vs structured arrays

This is where the two libraries diverge most visibly in day-to-day code, and it has real consequences at scale. SWR uses plain strings as cache keys — typically the URL itself. It’s immediately intuitive:

const { data } = useSWR('/api/todos?status=done', fetcher)

The simplicity is the point. Most SWR apps just use the URL as the key, so invalidating after a mutation means calling mutate('/api/todos?status=done'). This works naturally for simple cases. It starts to crack, however, when multiple different queries return the same underlying resource under different keys, or when you need to invalidate a whole family of queries at once — say, everything related to a user — without knowing every exact string in advance.

TanStack Query uses arrays as keys, with deterministic serialization under the hood:

const { data } = useQuery({
  queryKey: ['todos', { status: 'done' }],
  queryFn: fetchTodos,
})

The array structure means the cache can be queried by prefix. You can invalidate every query that starts with ['todos'] — regardless of what filters or variables follow - with a single call to invalidateQueries({ queryKey: ['todos'] }). You can also use filter functions to match queries against custom conditions. For a codebase with dozens of related queries, this is transformative: a single mutation can cleanly sweep out an entire slice of the cache without you tracking down every individual key string.

The tradeoff is verbosity. TanStack Query key arrays require more ceremony to write and read, and teams that don’t establish a consistent key naming convention early will pay for it later. SWR’s string keys are easier to grep, easier to reason about in isolation, and harder to accidentally misshape. For a smaller app, that simplicity wins. For a larger one, TanStack Query’s structured approach is worth the overhead.

Which one should you reach for?

The honest answer is that you can’t go wrong with either. But if you want a heuristic:

Reach for SWR when

  • You want minimal API surface
  • Your app is primarily read-heavy
  • You’re working in a Next.js / Vercel project
  • Onboarding speed matters
  • Bundle size is a constraint

Reach for TanStack Query when

  • You have complex mutation flows
  • Better query key handling
  • You need fine-grained cache control
  • You’re working across multiple frameworks
  • You want dedicated DevTools
  • Offline support is a requirement

메타데이터
post_id
7579d7fad1c8
slug
tanstack-query-vs-swr-choosing-the-right-data-fetching-library-7579d7fad1c8
url
https://medium.com/@ujevicigor/tanstack-query-vs-swr-choosing-the-right-data-fetching-library-7579d7fad1c8
canonical_url
https://medium.com/@ujevicigor/tanstack-query-vs-swr-choosing-the-right-data-fetching-library-7579d7fad1c8
author_url
https://medium.com/@ujevicigor
status
ok
fetched_at
2026-06-26 03:39:16