Skip to main content

TanStack Query v5 vs SWR v3 vs RTK Query 2026

·PkgPulse Team
0

TL;DR

Server state management has matured into a distinct category from client state. TanStack Query v5 (formerly React Query) is the most feature-complete option — devtools, mutation lifecycle, optimistic updates, prefetching, and SSR support with fine-grained control. SWR v3 by Vercel is the minimalist choice — small bundle, simple API, convention-over-configuration, especially elegant with Next.js. RTK Query is the right choice if you're already using Redux Toolkit — it colocates API definitions with your Redux store and handles loading/error states via slice patterns. In 2026, TanStack Query v5 wins for most use cases outside the Redux and Vercel/SWR ecosystems.

Key Takeaways

  • TanStack Query v5: 42M weekly downloads, 43K GitHub stars, v5 introduced simplified mutation API, removed callbacks from useQuery, TypeScript-first
  • SWR v3: 30M weekly downloads, 30K GitHub stars, 4KB gzipped, first-class Next.js integration, simple mental model
  • RTK Query: ships with Redux Toolkit (15M weekly downloads), zero additional dependencies if you use Redux, endpoints-as-config pattern
  • Bundle size: SWR ~4KB < TanStack Query ~13KB < RTK Query ~varies (Redux Toolkit base ~12KB)
  • Mutation API: TanStack Query's useMutation is the most comprehensive; SWR's mutate is simpler but less ergonomic for forms
  • Framework support: TanStack Query supports React, Vue, Solid, Svelte, Angular; SWR and RTK Query are React-focused

The Server State Problem

Before React Query (now TanStack Query) popularized the concept, most React apps mixed server state and client state in a single Redux store. Server state is fundamentally different: it's owned by a remote server, can be stale, needs cache invalidation, benefits from background refresh, and can fail. Treating it like client state (user preferences, UI toggles) creates complicated loading/error handling and cache invalidation logic.

All three libraries in this comparison are server state libraries — they manage the lifecycle of data fetched from an API with caching, deduplication, and background refresh baked in.


TanStack Query v5

TanStack Query v5 (formerly React Query) released in late 2023 with a significant API overhaul focused on TypeScript ergonomics and simplification.

Key v5 Changes From v4

// v4 (deprecated patterns):
const query = useQuery(['posts', id], () => fetchPost(id), {
  onSuccess: (data) => console.log(data),  // ❌ removed in v5
  onError: (err) => console.error(err),    // ❌ removed in v5
})

// v5: unified queryKey + queryFn object syntax
const query = useQuery({
  queryKey: ['posts', id],
  queryFn: () => fetchPost(id),
})
// Side effects → use useEffect watching data/error
useEffect(() => {
  if (query.data) console.log(query.data)
}, [query.data])

The removal of onSuccess/onError callbacks from useQuery was controversial but forced cleaner side-effect patterns.

Basic Data Fetching

import { useQuery, useQueryClient } from '@tanstack/react-query'

interface Post {
  id: number
  title: string
  body: string
  userId: number
}

function PostDetail({ postId }: { postId: number }) {
  const { data, isLoading, isError, error } = useQuery<Post>({
    queryKey: ['posts', postId],
    queryFn: async () => {
      const res = await fetch(`/api/posts/${postId}`)
      if (!res.ok) throw new Error('Failed to fetch post')
      return res.json()
    },
    staleTime: 60_000,      // Consider fresh for 60 seconds
    gcTime: 5 * 60_000,     // Keep in cache for 5 minutes (gcTime replaces cacheTime in v5)
  })

  if (isLoading) return <Skeleton />
  if (isError) return <ErrorMessage error={error} />
  return <Article post={data} />
}

Mutations with Optimistic Updates

TanStack Query's mutation API is the most expressive:

const queryClient = useQueryClient()

const updatePost = useMutation({
  mutationFn: (updated: Partial<Post>) =>
    fetch(`/api/posts/${postId}`, {
      method: 'PATCH',
      body: JSON.stringify(updated),
    }).then(r => r.json()),

  // Optimistic update
  onMutate: async (updated) => {
    await queryClient.cancelQueries({ queryKey: ['posts', postId] })
    const previous = queryClient.getQueryData<Post>(['posts', postId])

    queryClient.setQueryData<Post>(['posts', postId], (old) => ({
      ...old!,
      ...updated,
    }))

    return { previous }  // rollback context
  },

  onError: (err, updated, context) => {
    // Roll back on error
    queryClient.setQueryData(['posts', postId], context?.previous)
  },

  onSettled: () => {
    // Always refetch after mutation to sync with server
    queryClient.invalidateQueries({ queryKey: ['posts', postId] })
  },
})

Prefetching and SSR

// app/posts/[id]/page.tsx (Next.js App Router)
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'

export default async function PostPage({ params }: { params: { id: string } }) {
  const queryClient = new QueryClient()

  await queryClient.prefetchQuery({
    queryKey: ['posts', params.id],
    queryFn: () => fetchPost(params.id),
  })

  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <PostDetail postId={params.id} />
    </HydrationBoundary>
  )
}

TanStack Query v5 in Production

TanStack Query is the most widely adopted data fetching library in the React ecosystem, with 42M weekly npm downloads. Companies like Notion, Netflix, and Vercel have built production systems on it. The library's stability across major versions is notable — the API surface changes carefully and the migration guides are thorough.

The DevTools are a significant developer experience advantage. The TanStack Query DevTools panel shows every active query, its status, cache contents, stale time remaining, and last-updated timestamp. Debugging cache behavior is trivial compared to either logging or Redux DevTools. For teams that spend significant time debugging data fetching bugs (stale data, race conditions, unexpected refetches), the DevTools reduce debug time substantially.

TanStack Query's staleTime configuration is the lever most teams under-configure. The default staleTime is 0, meaning every time a component mounts it marks its queries as stale and triggers a background refetch. For data that doesn't change frequently (user profiles, configuration data, reference data), setting staleTime: 5 * 60 * 1000 (5 minutes) dramatically reduces API calls and perceived latency without a user-visible quality regression. The right default is usually staleTime: Infinity for reference data and staleTime: 30000 (30 seconds) for dynamic content.

TanStack Query's mutation API handles optimistic updates more elegantly than any alternative. The onMutate, onError, and onSettled lifecycle hooks on useMutation let you update the cache optimistically before the server confirms, roll back on error, and refetch to synchronize after completion. This pattern enables instant UI feedback for operations like toggling a like button, reordering a list, or editing a form field — essential for applications where perceived performance matters.


SWR v3

SWR (stale-while-revalidate) is Vercel's data fetching library — minimal, fast, and deeply integrated with Next.js. v3 refined the TypeScript experience and added middleware support.

Basic Usage

import useSWR from 'swr'

const fetcher = (url: string) => fetch(url).then(r => r.json())

function Profile({ userId }: { userId: string }) {
  const { data, error, isLoading } = useSWR<User>(
    `/api/users/${userId}`,
    fetcher,
    {
      refreshInterval: 30_000,    // poll every 30s
      revalidateOnFocus: true,    // refresh when tab regains focus
      dedupingInterval: 5_000,    // deduplicate same-key requests within 5s
    }
  )

  if (isLoading) return <Spinner />
  if (error) return <Error />
  return <UserCard user={data} />
}

Mutations and Cache Updates

SWR's mutation approach is simpler but more manual:

import useSWR, { mutate } from 'swr'

function EditProfile() {
  const { data: user } = useSWR<User>('/api/user', fetcher)

  const updateName = async (newName: string) => {
    // Optimistic update — update cache immediately, revalidate after
    await mutate(
      '/api/user',
      async (current: User) => {
        await fetch('/api/user', {
          method: 'PATCH',
          body: JSON.stringify({ name: newName }),
        })
        return { ...current, name: newName }
      },
      { optimisticData: { ...user!, name: newName } }
    )
  }

  return (
    <input
      defaultValue={user?.name}
      onBlur={(e) => updateName(e.target.value)}
    />
  )
}

SWR's mutate is elegant for simple updates but becomes verbose for complex optimistic UI patterns.

Middleware

SWR v3 added a proper middleware system:

// Logger middleware
const logger = (useSWRNext: SWRHook) => (key, fetcher, config) => {
  const extendedFetcher = (...args: unknown[]) => {
    console.log('SWR request:', key)
    return fetcher!(...args)
  }
  return useSWRNext(key, extendedFetcher, config)
}

// Error retry middleware with backoff
const retry = (useSWRNext: SWRHook) => (key, fetcher, config) => {
  const retryFetcher = async (...args: unknown[]) => {
    try {
      return await fetcher!(...args)
    } catch (err) {
      // Implement exponential backoff
      await new Promise(resolve => setTimeout(resolve, 1000))
      return fetcher!(...args)
    }
  }
  return useSWRNext(key, retryFetcher, config)
}

// Apply globally
const { data } = useSWR('/api/data', fetcher, { use: [logger, retry] })

RTK Query

RTK Query ships inside Redux Toolkit and is the right choice when your app already uses Redux for client state. It colocates API endpoint definitions with your store configuration.

Setup

// store/api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

export const postsApi = createApi({
  reducerPath: 'postsApi',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  tagTypes: ['Post', 'User'],

  endpoints: (builder) => ({
    getPosts: builder.query<Post[], void>({
      query: () => '/posts',
      providesTags: ['Post'],
    }),
    getPost: builder.query<Post, number>({
      query: (id) => `/posts/${id}`,
      providesTags: (result, error, id) => [{ type: 'Post', id }],
    }),
    updatePost: builder.mutation<Post, Partial<Post> & { id: number }>({
      query: ({ id, ...patch }) => ({
        url: `/posts/${id}`,
        method: 'PATCH',
        body: patch,
      }),
      invalidatesTags: (result, error, { id }) => [{ type: 'Post', id }],
    }),
  }),
})

export const { useGetPostsQuery, useGetPostQuery, useUpdatePostMutation } = postsApi

Usage in Components

function PostList() {
  const { data: posts, isLoading } = useGetPostsQuery()
  const [updatePost, { isLoading: isUpdating }] = useUpdatePostMutation()

  const handleUpdate = async (id: number, title: string) => {
    try {
      await updatePost({ id, title }).unwrap()
      // Success — RTK Query auto-invalidates 'Post' tag, re-fetches list
    } catch (error) {
      console.error('Update failed', error)
    }
  }

  if (isLoading) return <Loading />
  return (
    <ul>
      {posts?.map((post) => (
        <PostItem key={post.id} post={post} onUpdate={handleUpdate} />
      ))}
    </ul>
  )
}

The invalidatesTags pattern is RTK Query's cache invalidation mechanism — when updatePost succeeds, any query that providesTags: ['Post'] is automatically refetched.

RTK Query Code Generation

One of RTK Query's most practical enterprise features is its OpenAPI code generation. If your API has an OpenAPI specification, the @rtk-query/codegen-openapi tool generates the entire API slice from the spec — endpoint definitions, TypeScript types, and hooks — automatically:

npx @rtk-query/codegen-openapi openapi-config.ts

This generates typed hooks for every endpoint in your API spec. When the API spec changes, re-running the generator updates your types and hooks automatically. For teams that maintain OpenAPI contracts between frontend and backend, this workflow eliminates the manual effort of keeping API client types in sync with the server implementation.

When to Avoid RTK Query

RTK Query is the right choice when you're already using Redux. It is the wrong choice if you are adopting Redux solely because you want RTK Query's features. The bundle overhead of Redux Toolkit (12-15KB gzip) is not justified for RTK Query alone when TanStack Query provides equivalent server state management in 13KB without requiring a global store architecture.

Teams that have migrated from Redux to RTK Query as the sole remaining use case for Redux often regret the architecture — they maintain Redux boilerplate (store setup, Provider, reducer combination) for the single purpose of server state caching, which TanStack Query does without any of that overhead. If Redux is already in your stack for client state management, RTK Query is excellent. If it's not, prefer TanStack Query.


Feature Comparison

FeatureTanStack Query v5SWR v3RTK Query
Bundle size (gzip)~13KB~4KB~12KB (with RTK)
Weekly downloads42M30Mpart of RTK (15M)
Framework supportReact, Vue, Solid, SvelteReact onlyReact only
Devtools✅ Excellent⚠️ Basic✅ Redux DevTools
Mutation API✅ Comprehensive⚠️ Simple✅ Good
Optimistic updates✅ First-class⚠️ Manual✅ Built-in
Infinite scrolluseInfiniteQueryuseSWRInfinite⚠️ Manual
Dependent queriesenabled flag✅ null key patternskip flag
Background refetch
SSR/Hydrationdehydrate/HydrationBoundarySWRConfig fallback✅ via Redux hydration
PrefetchingprefetchQuery⚠️ preload (limited)initiate
TypeScript✅ First-class✅ Good✅ First-class

When to Choose Each

Choose TanStack Query v5 if:

  • You're starting a new React (or Vue/Solid/Svelte) project
  • You need complex mutation patterns with optimistic UI
  • Devtools are important for debugging
  • You want the richest query lifecycle control

Choose SWR v3 if:

  • Bundle size is critical (mobile, edge)
  • Your app is primarily Next.js on the Vercel platform
  • Your data fetching is relatively simple (mostly reads)
  • You prefer Vercel's conventions and ecosystem

Choose RTK Query if:

  • You already use Redux Toolkit for client state
  • You want a single store for both server and client state
  • Your team knows Redux patterns well
  • The endpoints-as-config pattern aligns with your architecture

Understanding the Architectural Differences

The three libraries embody different philosophies about where server state should live.

TanStack Query treats server state as entirely separate from client state. There is no global store — the query cache is an internal implementation detail accessed through hooks. This separation reflects the insight that server state and client state have fundamentally different characteristics: server state has latency, can become stale, needs background refresh, and may change on the server while the client is rendered. Client state (UI state like modal visibility, selected items, theme preference) has none of these characteristics. Mixing them into one store creates unnecessary complexity.

The practical implication is that TanStack Query works best when paired with a minimal client state solution. Most applications using TanStack Query use React Context or Zustand for the small amount of genuine client state (theme, user preferences, active selections) and let TanStack Query handle everything that comes from the API. The Zustand vs Jotai vs Nanostores micro state management 2026 comparison is relevant for teams choosing what to pair with TanStack Query.

SWR takes a pragmatic approach — it does one thing well and gets out of your way. The useSWR hook signature (key, fetcher) is deliberately minimal. You provide a unique key for the data and a fetcher function, and SWR handles caching, deduplication, and revalidation. There's no configuration of cache policies, stale times, or garbage collection unless you need it.

SWR's tight integration with Next.js (both developed by Vercel) means it has excellent SSR hydration support and good alignment with React Server Components patterns. For applications built on the Next.js App Router, SWR's SWRConfig for providing fallback data from server components aligns well with the framework's patterns.

RTK Query is the pragmatist solution for teams already using Redux. Redux's global store model means RTK Query's cache is part of the Redux state tree — you can inspect it in Redux DevTools, rehydrate it from SSR, and select from it with Redux selectors alongside your client state. The trade-off is that RTK requires you to define your API surface upfront as a set of endpoints, which is a different mental model than TanStack Query's hook-per-query pattern.

Cache Invalidation in Practice

Cache invalidation is often cited as one of the hardest problems in software engineering, and data fetching libraries solve it in meaningfully different ways.

TanStack Query uses query keys for invalidation. queryClient.invalidateQueries({ queryKey: ['posts'] }) marks all queries starting with ['posts'] as stale and triggers a background refetch for any active queries. The array-based key structure enables hierarchical invalidation — invalidating ['posts'] also invalidates ['posts', userId] and ['posts', userId, 'comments']. This hierarchical model is particularly powerful for applications with complex data dependencies.

SWR uses mutate(key) for cache invalidation. The key matches the same key string used in useSWR. For complex invalidation patterns (invalidating all queries that match a pattern), SWR v2.2+ provides mutate(key => shouldRevalidate(key)) with a predicate function. The mechanism is simpler than TanStack Query's but sufficient for most use cases.

RTK Query uses tags for invalidation. When a mutation runs, its invalidatesTags array causes any queries with matching providesTags to be refetched. The tag system requires upfront planning of your cache topology but makes the invalidation behavior explicit and type-safe.

TypeScript Integration

TypeScript support is where TanStack Query v5 made its most significant improvement over v4. The query options object (introduced in v5) enables end-to-end type inference without explicit type parameters in most cases:

// v5: type-safe without explicit generics
const { data } = useQuery(userQueryOptions(userId))
// data: User | undefined — inferred from queryOptions

SWR's TypeScript experience is good but requires explicit generic parameters in some cases. The fetcher function return type drives the inferred data type, which works naturally for most use cases.

RTK Query's endpoint definitions are the most explicit TypeScript surface — you define input and output types per endpoint, and the generated hooks are fully typed. For large APIs with many endpoints, the upfront investment in endpoint type definitions pays off in exhaustive type checking.

Next.js Integration

All three libraries integrate well with Next.js, but with different patterns in the App Router era.

For the Next.js App Router with React Server Components, the recommended approach is to fetch data in Server Components (no data fetching library needed) and use TanStack Query or SWR only for client-side dynamic data that needs caching, optimistic updates, or real-time polling. Mixing Server Component data fetching with TanStack Query in Client Components for the dynamic parts gives you the best of both worlds.

For teams building full-stack applications with Next.js, the best Next.js auth solutions 2026 covers how authentication state management interacts with server state management — a common source of complexity when both need to be coordinated.

One practical consideration: when hydrating server-fetched data into TanStack Query on the client, the HydrationBoundary and dehydrate APIs avoid double-fetching. SWR's fallback option in SWRConfig accomplishes the same goal with less boilerplate. For applications where initial page load performance matters, verifying your hydration setup is correct avoids the waterfall of a server fetch followed by an immediate client refetch of the same data.


Infinite Loading and Pagination

All three libraries handle infinite scroll, but with different ergonomics:

TanStack Query — useInfiniteQuery

const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery({
  queryKey: ['posts'],
  queryFn: ({ pageParam = 0 }) =>
    fetch(`/api/posts?cursor=${pageParam}&limit=20`).then(r => r.json()),
  getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
  initialPageParam: 0,
})

// data.pages is an array of page results
const allPosts = data?.pages.flatMap((page) => page.items) ?? []

SWR — useSWRInfinite

import useSWRInfinite from 'swr/infinite'

const getKey = (pageIndex: number, previousPageData: PageData | null) => {
  if (previousPageData && !previousPageData.nextCursor) return null  // end
  return `/api/posts?cursor=${previousPageData?.nextCursor ?? ''}&limit=20`
}

const { data, size, setSize, isLoading } = useSWRInfinite<PageData>(
  getKey,
  fetcher
)

const allPosts = data?.flatMap((page) => page.items) ?? []
const loadMore = () => setSize(size + 1)

RTK Query — Infinite Loading Pattern

RTK Query doesn't have a built-in infinite query — you typically use cursor/page state in a parent component and merge results:

// Manual approach for RTK Query infinite scroll
function InfinitePostList() {
  const [cursor, setCursor] = useState<string | undefined>()
  const [allPosts, setAllPosts] = useState<Post[]>([])

  const { data } = useGetPostsQuery({ cursor, limit: 20 })

  useEffect(() => {
    if (data?.items) setAllPosts((prev) => [...prev, ...data.items])
  }, [data])

  return (
    <>
      {allPosts.map((post) => <PostCard key={post.id} post={post} />)}
      {data?.nextCursor && (
        <button onClick={() => setCursor(data.nextCursor)}>Load More</button>
      )}
    </>
  )
}

TanStack Query's useInfiniteQuery is the most ergonomic; SWR's useSWRInfinite is close; RTK Query requires manual state management for this pattern.

Ecosystem & Community

TanStack Query has the broadest ecosystem of the three. TkDodo's blog (the core maintainer's writing) has become the definitive resource for React server state patterns, and the TanStack Discord is one of the most active JavaScript communities. The TanStack umbrella also includes TanStack Table, TanStack Router, and TanStack Virtual — making it a natural choice for teams that want a cohesive set of headless primitives across their application.

SWR's ecosystem is deliberately minimal — it pairs with the Next.js and Vercel ecosystem rather than building its own. The Vercel team maintains it alongside Next.js, which means SWR features often arrive in coordination with Next.js App Router improvements. The lack of a standalone ecosystem is a feature for teams that want a library with a small surface area.

RTK Query inherits Redux Toolkit's enormous community. The Redux ecosystem includes browser devtools extensions, testing utilities, and years of patterns documented on the Redux documentation site. For teams with existing Redux investments, RTK Query means no context switching between state management paradigms.

Real-World Adoption

TanStack Query is the default choice for React server state in 2026. It's recommended in the React docs ecosystem, used by Remix and other React meta-frameworks, and integrated into various component library documentation sites. At 42M weekly downloads, it's one of the most-installed React packages period.

SWR is disproportionately popular in the Next.js ecosystem. Vercel's own example applications and documentation use SWR extensively. Companies deploying Next.js applications to Vercel often reach for SWR first because it integrates naturally with the platform's data model and the existing documentation is thorough.

RTK Query is most prevalent in enterprise React applications that adopted Redux Toolkit as their state management foundation. Companies that invested heavily in Redux patterns (particularly before server state libraries existed) find RTK Query provides a migration path that preserves their existing architectural decisions.

Developer Experience Deep Dive

TanStack Query's devtools are a significant developer experience advantage. The React Query Devtools panel shows the cache state, query keys, data freshness, and refetch history in real time. Debugging cache invalidation issues — a common source of data synchronization bugs — is dramatically easier with the devtools than with any other approach. The TypeScript experience is also first-class: query key factories, typed mutations, and inferred return types all work out of the box.

SWR's developer experience prioritizes simplicity. The mental model — string key, fetcher function, optional config — fits in one line of documentation. Debugging is more manual (network tab and React DevTools), but for simpler data fetching patterns the lack of abstraction is an advantage. The smaller API surface means less to learn and fewer ways things can go wrong.

RTK Query's developer experience is tied to the Redux DevTools, which provide time-travel debugging and full state inspection. The endpoint-as-config pattern is more verbose to set up initially, but in large applications the colocated API definitions are easier to reason about than scattered useQuery calls. The generated hooks (useGetPostsQuery, useUpdatePostMutation) are clearly named and self-documenting.

Final Verdict 2026

For new React projects without existing Redux investments, TanStack Query v5 is the default choice. The rich mutation lifecycle, excellent devtools, and cross-framework support make it the most capable server state library in the JavaScript ecosystem. The v5 API cleanup removed some footguns and made TypeScript integration cleaner.

For Next.js applications deployed to Vercel, SWR remains a compelling choice due to its minimal bundle size, seamless integration with Next.js data patterns, and Vercel's official support. The 4KB bundle is genuinely meaningful at the edge, and for read-heavy applications SWR's simplicity is a feature rather than a limitation.

RTK Query is the right choice exactly when it sounds right: when your application uses Redux Toolkit and you want a unified state management story. Adding TanStack Query to a Redux application creates two distinct patterns for data management — RTK Query avoids that complexity by collocating server state management with the Redux store.


Methodology

  • Download data from npm registry API, March 2026
  • TanStack Query v5 docs: tanstack.com/query/v5
  • SWR v3 docs: swr.vercel.app
  • RTK Query docs: redux-toolkit.js.org/rtk-query
  • Bundle sizes via bundlephobia.com, March 2026

Compare these packages on PkgPulse: TanStack Query vs SWR.

Related: Zustand vs Jotai vs Nanostores Micro State Management 2026 · Best React Form Libraries 2026

For more React ecosystem comparisons, see best React component libraries 2026. If you're evaluating backend solutions that pair with these data fetching libraries, Hono vs Elysia 2026 covers the API framework layer.

Practical Decision Framework

Server state and client state are different problems. The biggest mistake teams make is choosing a data fetching library based on its popularity rather than its fit for their specific access patterns.

Choose TanStack Query v5 if your application has complex data requirements: multiple dependent queries, optimistic updates with rollback, pagination with prefetching, or shared query state across many components. The invalidation model, background refetch controls, and fine-grained cache configuration are worth the learning curve when you need them.

Choose SWR v3 if you have straightforward GET request caching needs and want minimum boilerplate. SWR's defaults — revalidate on focus, stale-while-revalidate pattern, simple mutation — cover a large fraction of real-world use cases with very little configuration. Its smaller bundle size also matters for applications where initial load performance is constrained.

Choose RTK Query if you are already committed to Redux Toolkit and want your server state in the same store as your client state. The code generation from OpenAPI specs is a practical advantage for API-heavy applications with typed contracts. Avoid it if you are not using Redux — the bundle overhead is not justified if you're adopting Redux solely for RTK Query.

Pick one library and use it consistently across the codebase. Mixing TanStack Query and SWR in the same application creates cache coherence problems and makes debugging harder.

If you are building a new application from scratch in 2026, TanStack Query v5 is the safest long-term bet. Its active development, large community, and cross-framework support mean it will remain relevant regardless of which rendering paradigm gains traction next. The v5 breaking changes were intentional investments in correctness and ergonomics that pay dividends over a multi-year codebase lifespan.

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.