Overview
Use TanStack Query for any React app with mutations, optimistic updates, paginated lists, or background refetch logic. Use SWR when the app is small, read-mostly, and already on Vercel where SWR’s defaults align with the platform. TanStack Query is the larger toolset; SWR is the lighter pattern. Both manage server state, not client state; pair either with Zustand or Redux for client state. See react-state-management for the wider rule set.
When TanStack Query wins
TanStack Query is the right pick for any React app with non-trivial cache, mutations, or background sync behavior.
- Mutations:
useMutationwithonSuccess,onError, andinvalidateQueriesis the canonical pattern. SWR hasmutatebut lacks the lifecycle hooks; teams end up writing wrappers. - Optimistic updates:
useMutation({ onMutate, onError, onSettled })gives a typed rollback path. SWR optimistic UI is doable but the API is thinner. - Query keys are arrays with structural equality;
['post', id]invalidates only that post. SWR’s string keys force string concatenation conventions. - DevTools are best-in-class: see every active query, its cache state, last fetch time, and stale status. SWR DevTools exist but are less mature.
- Pagination, infinite scroll, prefetching, and dependent queries are first-class:
useInfiniteQuery,prefetchQuery,enabledflag. - Works outside React:
@tanstack/query-coreruns in Vue, Solid, and Svelte. Same mental model across the stack. - Suspense and Server Components support is stable in v5; SWR’s Suspense story is functional but quieter.
When SWR wins
SWR is the right pick for small read-mostly apps, especially those already on Vercel.
- Smaller surface:
useSWR(key, fetcher)is the whole API for 80 percent of use cases. New contributors are productive in an afternoon. - Bundle: SWR is roughly 4 KB gzipped; TanStack Query is 13 to 15 KB. On a content site with one or two data hooks, that gap shows up in Time-to-Interactive.
- Vercel alignment: SWR is Vercel’s library. Examples in the Next.js docs default to it; Vercel-hosted templates ship it pre-wired. The integration cost is zero.
- Stale-while-revalidate semantics map cleanly to the HTTP cache header pattern. If the API already sets
Cache-Control: stale-while-revalidate, SWR mirrors it. - Focus revalidation and reconnect refetch are default-on with sensible behavior; TanStack Query has the same but exposes more knobs to misconfigure.
- For a dashboard with a few read endpoints and no mutations: SWR is the smaller pick by every measure.
Trade-offs at a glance
| Dimension | TanStack Query | SWR |
|---|---|---|
| Bundle (gzipped) | 13 to 15 KB | 4 KB |
| Query keys | Arrays with structural equality | Strings or arrays via the key function |
| Mutations | First-class with lifecycle hooks | mutate() plus manual wiring |
| Optimistic updates | Typed; rollback path baked in | Manual; less ergonomic |
| Devtools | Excellent; visualizes every query | Functional; smaller feature set |
| Infinite scroll | useInfiniteQuery | useSWRInfinite |
| Suspense | Stable in v5 | Supported; less documented |
| Server Components | Pre-fetch and hydrate pattern documented | Works; less canonical guidance |
| Cross-framework | React, Vue, Solid, Svelte | React (and Next.js) |
| Hiring familiarity | High in React Native and dashboard teams | High in Next.js teams |
| Sweet spot | Apps with mutations and complex cache | Read-mostly content and dashboards |
Migration cost
SWR-to-TanStack Query is the common direction once mutations or invalidation logic grows. The reverse is rare.
- SWR to TanStack Query: replace
useSWR(key, fetcher)withuseQuery({ queryKey, queryFn })andmutate(key)withqueryClient.invalidateQueries({ queryKey }). The shapes are similar enough that a codemod handles roughly 70 percent. Plan one engineer-day per 30 hooks. - TanStack Query to SWR: only worth it on a small app where the bundle line item shows up. Rewrite mutations to imperative
mutate()calls and drop most lifecycle code. Plan one engineer-day per 30 hooks. - Cheaper path: stay on the current library. The pain that justifies the switch usually shows up at the same time as a feature redesign; bundle the rewrite into that.
Recommendation
- New React app with any mutations beyond a single form: TanStack Query.
- New small Next.js app on Vercel that is mostly reads: SWR. The defaults align.
- Dashboard with infinite scroll, optimistic edits, or background sync: TanStack Query.
- Marketing site with one or two data hooks: neither; use Server Components and skip client cache; see server-vs-client-components.
- React Native app: TanStack Query. The DevTools and offline patterns are documented for mobile.
- Existing SWR codebase that grew complex: migrate the mutations only, leave reads on SWR. Two libraries coexist fine.
- See react-state-management for the wider state strategy and zustand-vs-redux for the client-state pair.