Which data fetching library should you use in your React project? SWR and TanStack Query are the two most popular choices right now. Both are excellent, but the right pick depends on your project's scale and complexity. After years of using both in production, here's my thorough comparison.
Table of Contents
- What is SWR?
- What is TanStack Query?
- Comparison Table
- Core Comparison
- Performance
- Code Examples
- When to Use Which?
- My Pick
What is SWR?
SWR is a React data fetching library built by Vercel. The name comes from the "Stale-While-Revalidate" HTTP caching strategy — it shows cached data first, then fetches fresh data in the background.
Key Strengths
SWR's biggest advantage is simplicity. You can write API calls declaratively, and it automatically handles caching, revalidation, and request deduplication. At just 4.2KB gzipped, it's incredibly lightweight.
Next.js integration is seamless since Vercel built both. ISR (Incremental Static Regeneration) and Server Components work naturally with SWR.
SWR v2 Updates
SWR v2 introduced the useSWRMutation hook, making POST/PUT/DELETE operations more structured. However, the mutation experience still doesn't match TanStack Query's depth.
What is TanStack Query?
TanStack Query (formerly React Query) went framework-agnostic starting with v5 (latest: 5.99.0). With 12.3M weekly downloads and 48K GitHub stars, it's widely adopted in larger projects.
TanStack Query v5 Strengths
TanStack Query offers depth of features: professional mutation management, advanced caching strategies, and automatic optimizations. The official DevTools make debugging significantly easier.
v5 brought major TypeScript inference improvements, automatic field tracking for selective re-renders, and official React Suspense support.
Bundle Size
At 13.4KB gzipped, it's larger than SWR. But considering modern web project sizes, the difference is negligible — especially given the additional functionality.
Comparison Table
| Feature | SWR | TanStack Query |
|---|---|---|
| Weekly Downloads | 4.9M | 12.3M |
| GitHub Stars | 32K | 48K |
| Bundle Size (gzip) | 4.2KB | 13.4KB |
| Mutations | useSWRMutation | useMutation (dedicated) |
| DevTools | None | Official |
| TypeScript | Basic | v5 Enhanced |
| React Suspense | Limited | useSuspenseQuery |
| Caching Strategy | Basic | Advanced |
| SSR/SSG | Next.js optimized | Good |
| Learning Curve | Low | Medium |
| Community | Active | Very Active |
Core Comparison
Bundle Size
SWR's 4.2KB is genuinely light. If you're building for mobile users with poor connectivity, this alone could be the deciding factor.
That said, the 9.2KB difference rarely impacts real-world performance. Optimizing a single image has far more impact.
Mutation API
With SWR, handling data mutations (POST, PUT, DELETE) still requires some manual work. You fetch data with useQuery and write separate fetch calls for mutations.
TanStack Query's useMutation was designed for mutations from the ground up — loading states, error handling, automatic retries, and optimistic updates are all built-in.
DevTools
Once you've used TanStack Query DevTools, it's hard to go back. You can visually inspect query states, cache, and request history. It's incredibly powerful for debugging complex data fetching logic.
SWR relies on React DevTools or console.log for debugging — a clear gap.
TypeScript Support
TanStack Query v5 has notably improved type inference. Query data types are automatically inferred, and only accessed fields trigger re-renders.
SWR has basic TypeScript support, but it's not as refined as TanStack Query v5.
Caching Strategy
SWR provides fundamental caching with the stale-while-revalidate pattern, which covers most use cases.
TanStack Query offers fine-grained control with options like staleTime, gcTime, and refetchInterval for complex caching requirements.
SSR & React Suspense
If you're on Next.js, SWR's SSR integration is very clean — it's Vercel's own library, after all.
TanStack Query also supports SSR well but requires a bit more configuration. For React Suspense, TanStack Query's useSuspenseQuery is more stable.
Performance
Render Optimization
TanStack Query v5's automatic field tracking is impressive. Components that use only part of the query data re-render only when that specific part changes. SWR always re-renders when any part of the data changes.
This can matter significantly in projects with large datasets.
Request Deduplication
Both libraries automatically merge concurrent requests to the same URL into a single network call. No winner here — both handle this well.
Caching Performance
SWR's stale-while-revalidate pattern is excellent for UX — show cached data instantly, refresh in the background. TanStack Query can be configured for the same behavior, though its defaults are more conservative.
Code Examples
Basic Query
SWR:
import useSWR from 'swr'
const fetcher = (url) => fetch(url).then(r => r.json())
export default function UserProfile() {
const { data, error, isLoading } = useSWR(
'/api/user',
fetcher
)
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error occurred</div>
return <div>Hello, {data.name}!</div>
}
TanStack Query:
import { useQuery } from '@tanstack/react-query'
export default function UserProfile() {
const { data, error, isLoading } = useQuery({
queryKey: ['user'],
queryFn: async () => {
const res = await fetch('/api/user')
return res.json()
}
})
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error occurred</div>
return <div>Hello, {data.name}!</div>
}
At the basic level, they're quite similar. SWR looks slightly simpler.
Mutation Handling
SWR:
import useSWR from 'swr'
export default function UpdateUser() {
const { data, mutate } = useSWR('/api/user', fetcher)
const updateUser = async (newName) => {
const res = await fetch('/api/user', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: newName })
})
const updated = await res.json()
mutate(updated, false)
}
return (
<button onClick={() => updateUser('Jake')}>
Change Name
</button>
)
}
TanStack Query:
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
export default function UpdateUser() {
const queryClient = useQueryClient()
const mutation = useMutation({
mutationFn: async (newName) => {
const res = await fetch('/api/user', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: newName })
})
return res.json()
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['user'] })
}
})
return (
<button
onClick={() => mutation.mutate('Jake')}
disabled={mutation.isPending}
>
{mutation.isPending ? 'Saving...' : 'Change Name'}
</button>
)
}
This is where the difference becomes clear. TanStack Query's useMutation systematically manages loading states, error handling, and cache invalidation.
When to Use Which?
Choose SWR When:
- Bundle size matters: Mobile-heavy apps with poor network conditions
- Next.js projects: Especially when using SSR/SSG extensively
- Simple data fetching: Straightforward CRUD with minimal mutations
- Rapid prototyping: Minimize learning time and ship fast
- Vercel ecosystem: Already deploying on Vercel with other Vercel libraries
Choose TanStack Query When:
- Complex data fetching: Heavy use of mutations, infinite queries, pagination
- Large-scale projects: Managing dozens of API endpoints
- Advanced caching: Complex caching strategies required
- DevTools needed: Debugging and state tracking are critical
- React Suspense: Want to leverage the latest React features
- Multi-framework: Potential to expand to Vue, Svelte, etc.
Decision Matrix
| Project Type | Recommendation |
|---|---|
| Next.js + Simple App | SWR |
| Next.js + Complex App | TanStack Query |
| React SPA + Small/Medium | SWR |
| React SPA + Large Scale | TanStack Query |
| Mobile Web (Bundle Min) | SWR |
| Enterprise App | TanStack Query |
My Pick
To be honest, I primarily use TanStack Query.
As a fullstack developer with 10 years of experience across multiple projects, SWR's simplicity looks appealing at first. But as projects grow, you start needing complex mutations, cache invalidation, and concurrent request handling. That's when TanStack Query truly shines.
DevTools alone is worth it — it significantly cuts debugging time and helps share data flow understanding with the team. TypeScript support in v5 is also excellent for type safety.
But SWR is great for lightweight side projects. When bundle size is constrained or you just need basic CRUD, SWR's simplicity is unmatched. For a Next.js blog or marketing site, SWR is more than enough.
The right choice depends on your context. Consider your project's complexity and your team's preferences.
Originally published on dev.Jake Blog. Written as of April 2026 — always check the latest versions as the React ecosystem evolves quickly.
Top comments (0)