Skip to main content
This comparison table strives to be as accurate and as unbiased as possible. If you use any of these libraries and feel the information could be improved, feel free to suggest changes.

Feature Legend

  • βœ… First-class, built-in, and ready to use with no added configuration or code
  • 🟑 Supported, but as an unofficial 3rd party or community library/contribution
  • πŸ”Ά Supported and documented, but requires extra user-code to implement
  • πŸ›‘ Not officially supported or documented

Quick Comparison

FeatureTanStack QuerySWRApollo ClientRTK QueryReact Router
Platform RequirementsReactReactReact, GraphQLReduxReact
Supported Query SyntaxPromise, REST, GraphQLPromise, REST, GraphQLGraphQL, AnyPromise, REST, GraphQLPromise, REST, GraphQL
Caching StrategyHierarchical Key β†’ ValueUnique Key β†’ ValueNormalized SchemaUnique Key β†’ ValueNested Route β†’ value
Cache Change DetectionDeep Compare (Stable)Deep Compare (Stable)Deep Compare (Unstable)Referential EqualityRoute Change
Data MemoizationFull Structural SharingIdentity (===)Normalized IdentityIdentity (===)Identity (===)

Core Features

Data Fetching & Caching

FeatureTanStack QuerySWRApollo ClientRTK QueryReact Router
Queriesβœ…βœ…βœ…βœ…βœ…
Mutationsβœ…βœ…βœ…βœ…βœ…
Cache Persistenceβœ…βœ…βœ…βœ…πŸ›‘ Active Routes Only
Devtoolsβœ…βœ…βœ…βœ…πŸ›‘
Auto Garbage Collectionβœ…πŸ›‘πŸ›‘βœ…N/A
Offline Cachingβœ…πŸ›‘βœ…πŸ”ΆπŸ›‘

Query Types

FeatureTanStack QuerySWRApollo ClientRTK QueryReact Router
Parallel Queriesβœ…βœ…βœ…βœ…βœ…
Dependent Queriesβœ…βœ…βœ…βœ…βœ…
Paginated Queriesβœ…βœ…βœ…βœ…βœ…
Infinite Queriesβœ…βœ…βœ…βœ…πŸ›‘
Bi-directional Infiniteβœ…πŸ”ΆπŸ”Άβœ…πŸ›‘
Infinite Query Refetchingβœ…βœ…πŸ›‘βœ…πŸ›‘

Advanced Features

FeatureTanStack QuerySWRApollo ClientRTK QueryReact Router
Lagged Query Dataβœ…βœ…βœ…βœ…βœ…
Selectorsβœ…πŸ›‘βœ…βœ…N/A
Initial Dataβœ…βœ…βœ…βœ…βœ…
Scroll Recoveryβœ…βœ…βœ…βœ…βœ…
Cache Manipulationβœ…βœ…βœ…βœ…πŸ›‘
Query Cancellationβœ…πŸ›‘πŸ›‘πŸ›‘βœ…
Partial Query Matchingβœ…πŸ”Άβœ…βœ…N/A

Refetching & Invalidation

FeatureTanStack QuerySWRApollo ClientRTK QueryReact Router
Polling/Intervalsβœ…βœ…βœ…βœ…πŸ›‘
Window Focus Refetchingβœ…βœ…πŸ›‘βœ…πŸ›‘
Network Status Refetchingβœ…βœ…βœ…βœ…πŸ›‘
Stale While Revalidateβœ…βœ…βœ…βœ…πŸ›‘
Stale Time Configurationβœ…πŸ›‘πŸ›‘βœ…πŸ›‘
Automatic Refetch After MutationπŸ”ΆπŸ”Άβœ…βœ…βœ…

Performance & Optimization

FeatureTanStack QuerySWRApollo ClientRTK QueryReact Router
Render Batching & Optimizationβœ…βœ…πŸ›‘βœ…βœ…
Outdated Query Dismissalβœ…βœ…βœ…βœ…βœ…
Normalized CachingπŸ›‘πŸ›‘βœ…πŸ›‘πŸ›‘

Mutations

FeatureTanStack QuerySWRApollo ClientRTK QueryReact Router
Mutation Hooksβœ…βœ…βœ…βœ…βœ…
Offline Mutation Supportβœ…πŸ›‘πŸŸ‘πŸ›‘πŸ›‘

Server-Side & Hydration

FeatureTanStack QuerySWRApollo ClientRTK QueryReact Router
Prefetching APIsβœ…βœ…βœ…βœ…βœ…
Cache Dehydration/Rehydrationβœ…πŸ›‘βœ…βœ…βœ…
React Suspenseβœ…βœ…βœ…πŸ›‘βœ…

Configuration & Setup

FeatureTanStack QuerySWRApollo ClientRTK QueryReact Router
API Definition LocationComponent, ExternalComponentGraphQL SchemaExternal ConfigRoute Tree
Pre-usage Configurationβœ…πŸ›‘βœ…βœ…βœ…
Abstracted/Agnostic Coreβœ…πŸ›‘βœ…βœ…πŸ›‘

Feature Explanations

Lagged Query Data

TanStack Query provides a way to continue to see an existing query’s data while the next query loads (similar to the UX that Suspense provides natively). This is extremely important when writing pagination UIs or infinite loading UIs where you don’t want to show a hard loading state whenever a new query is requested.
Other libraries without this feature will render a hard loading state for new queries (unless prefetched), while the new query loads.

Render Optimization

TanStack Query has excellent rendering performance. By default, it automatically tracks which fields are accessed and only re-renders if one of them changes. You can configure this behavior:
  • Set notifyOnChangeProps to 'all' to re-render whenever the query updates
  • Set notifyOnChangeProps to ['data', 'error'] to only re-render when specific properties change
TanStack Query also batches updates together to ensure your application only re-renders once when multiple components use the same query.

Partial Query Matching

Because TanStack Query uses deterministic query key serialization, you can manipulate variable groups of queries without knowing each individual query key:
// Refetch all queries starting with 'todos'
queryClient.refetchQueries({ queryKey: ['todos'] })

// Invalidate specific queries with variables
queryClient.invalidateQueries({ queryKey: ['todos', { status: 'done' }] })
You can also use filter functions to match queries based on custom conditions.

Pre-usage Query Configuration

Queries and mutations can be fully configured with defaults before use. For example:
queryClient.setQueryDefaults(['todos'], {
  queryFn: fetchTodos,
  staleTime: 60_000,
})

// Later, only the key is needed
const { data } = useQuery({ queryKey: ['todos'] })
SWR only supports a global default fetcher, not per-query configuration.

Automatic Refetch After Mutation

For truly automatic refetching to happen after a mutation, a schema is necessary (like GraphQL provides) along with heuristics that help the library identify individual entities and entity types. TanStack Query requires manual invalidation but provides powerful tools to make this easy:
const mutation = useMutation({
  mutationFn: updateTodo,
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['todos'] })
  },
})

Normalized Caching

TanStack Query, SWR, and RTK Query do not support automatic normalized caching, which stores entities in a flat architecture to avoid data duplication.
Apollo Client excels at normalized caching due to its GraphQL schema awareness.

Bundle Size Comparison

Bundle sizes matter for performance. Here’s how the libraries compare:
  • TanStack Query: ~13 KB (minified + gzipped)
  • SWR: ~5 KB (minified + gzipped)
  • Apollo Client: ~33 KB (minified + gzipped)
  • RTK Query: Included with Redux Toolkit (~18 KB)
  • React Router: ~25 KB (minified + gzipped)
Bundle sizes are approximate and may vary with versions. Check bundlephobia.com for current sizes.

Which Library Should You Choose?

Choose TanStack Query if:

  • You need a powerful, flexible async state management solution
  • You want excellent TypeScript support
  • You need advanced features like infinite queries, prefetching, and optimistic updates
  • You value great DevTools and documentation

Choose SWR if:

  • You want a minimal, lightweight solution
  • Your use case is simple data fetching
  • You prefer Vercel’s ecosystem

Choose Apollo Client if:

  • You’re using GraphQL
  • You need normalized caching
  • You benefit from the GraphQL ecosystem

Choose RTK Query if:

  • You’re already using Redux
  • You want Redux DevTools integration
  • You prefer the Redux architecture

Choose React Router if:

  • Your data fetching is tightly coupled to routing
  • You want built-in loader patterns
  • You’re using Remix or similar frameworks
Each library has its strengths. TanStack Query provides the best balance of features, performance, and developer experience for most applications.

Build docs developers (and LLMs) love