Premium Website Templates — Designer-Level UI for Modern Brands | ProofMatcher

React Query vs SWR: Data Fetching in 2026

The Data Fetching Library Problem in React

React doesn't provide built-in data fetching. The naive approach — useEffect + useState — works for simple cases but fails to handle loading states, error states, caching, refetching, background updates, and optimistic mutations correctly without significant boilerplate. React Query (now TanStack Query) and SWR both solve this problem with a declarative API that handles the complexity automatically. Understanding the differences helps you choose the right tool for your project's complexity level.

SWR: The Simpler Choice

SWR (Stale-While-Revalidate) from Vercel is the simpler of the two libraries. The API surface is small — the primary hook is useSWR(key, fetcher) which returns data, error, and isLoading. SWR handles caching, deduplication of identical requests, background revalidation, focus refetching, and polling. The cache is global by default — all components using the same key share the same cached data. Mutations use mutate(key, data) to update the cache optimistically or after a server response.

SWR is the right choice for: simpler applications with primarily read-heavy data fetching, projects already using Vercel/Next.js where SWR integrates naturally, and teams that prioritize a minimal API surface and smaller bundle size.

TanStack Query: The Comprehensive Choice

TanStack Query (formerly React Query) provides a more comprehensive data synchronization solution. Beyond the basic useQuery hook, it offers useMutation with built-in optimistic updates and rollback, useInfiniteQuery for paginated data, query invalidation and refetching by query key patterns, dependent queries, query prefetching, and a powerful DevTools extension. The query key system is more sophisticated — queries can be invalidated by prefix, enabling patterns like "refetch all user data after a profile update."

TanStack Query is the right choice for: complex applications with heavy mutation patterns (forms, multi-step workflows), applications requiring sophisticated cache invalidation (e.g., invalidate all queries related to a user after logout), and teams that need the full feature set for production SaaS applications.

Bundle Size and Performance

SWR: ~4KB gzipped. TanStack Query: ~13KB gzipped. For most applications, the size difference is negligible relative to the application bundle. Both libraries use React's concurrent features correctly and don't cause unnecessary re-renders. TanStack Query's larger size reflects its significantly larger feature set rather than implementation bloat.

The Verdict for 2026

Default to TanStack Query for new production SaaS applications — the additional features justify the marginal size cost, and the DevTools alone save significant debugging time. Use SWR for simpler read-heavy applications, Next.js projects that benefit from Vercel's native integration, or when bundle size is a critical constraint. ProofMatcher's React templates include TanStack Query pre-configured — download free at proofmatcher.com.

Mutations and Cache Invalidation

Reading data is only half the job. When users create, update, or delete something, the cached data must be refreshed. TanStack Query's useMutation hook runs the request and exposes its status, and in its onSuccess callback you call queryClient.invalidateQueries({ queryKey: ["todos"] }) to mark related queries as stale so they refetch. SWR provides useSWRMutation for the request and the mutate function to update or revalidate cached keys. Both work well; TanStack Query's key-based invalidation scales better when many screens share related data, because you can invalidate a whole group of queries by a common key prefix.

Designing Query Keys

Query keys decide what is cached together, so design them deliberately. Use arrays that go from general to specific, such as ["projects"], ["projects", projectId], and ["projects", projectId, "tasks", { status: "open" }]. Include every variable the request depends on, including filters and page numbers, so different inputs never share a cache entry. Many teams keep keys in a small factory object, such as projectKeys.detail(id), so the same key is never typed differently in two places.

Optimistic Updates

For fast-feeling interfaces, update the cache before the server responds. With TanStack Query, you cancel in-flight queries for the key, save the previous data, write the expected new data into the cache in onMutate, and restore the saved data in onError if the request fails. A simpler alternative in v5 is to render the pending mutation's variables directly in the UI while it runs. SWR supports optimistic updates through the optimisticData and rollbackOnError options of mutate. Use optimistic updates for small, reversible actions like toggles and likes, and show clear errors when a rollback happens.

Understanding staleTime and gcTime

TanStack Query's two most important options are often confused. staleTime is how long data is considered fresh; while fresh, it is served from the cache without refetching. The default is zero, which means data is refetched in the background whenever a component mounts or the window regains focus. gcTime, called cacheTime before version 5, is how long unused data stays in memory after the last component using it unmounts. For data that rarely changes, such as a list of countries, set a long staleTime to avoid unnecessary requests.

Pagination and Infinite Scroll

Both libraries support paginated data. TanStack Query's useInfiniteQuery manages pages with getNextPageParam, which reads the cursor for the next page from the last response, and returns all loaded pages plus a fetchNextPage function to call when the user scrolls near the end. SWR offers useSWRInfinite with a similar model. For classic numbered pagination, include the page in the query key and use the placeholderData option with keepPreviousData so the previous page stays visible while the next one loads.

Server Rendering and Next.js

With server rendering, you want data fetched on the server and available immediately on the client. TanStack Query supports this by prefetching queries on the server and passing the dehydrated cache to a HydrationBoundary, so the client starts with data already in place. SWR uses a fallback object in SWRConfig for the same purpose. In the Next.js App Router, consider whether a server component can fetch the data directly; you may only need a client-side library for data that must refresh or change interactively.

Developer Tools and Common Mistakes

TanStack Query ships dedicated Devtools that show every query, its status, and its cached data, which is invaluable for debugging. Common mistakes with both libraries include copying fetched data into useState, which creates a second, stale copy; creating a new query client on every render; and forgetting to include variables in the query key. Keep fetched data in the library's cache, derive what you need during render, and let the library handle refetching.

Error Handling and Retries

Both libraries retry failed requests automatically, but with different defaults. TanStack Query retries failed queries three times with exponential backoff before showing an error, which is helpful for flaky networks but can delay error messages; set retry: false for requests where a failure is final, such as a 404. SWR retries errors using exponential backoff as well and exposes errorRetryCount to limit attempts. Both return an error object you can render inline. For a cleaner component tree, TanStack Query can throw errors to the nearest React error boundary with the throwOnError option, and both libraries support React Suspense for loading states.

Choosing in Practice

Choose SWR when your app mostly reads data, you want the smallest API and bundle, and you already use Vercel's ecosystem. Choose TanStack Query when your app has many mutations, complex cache invalidation, infinite lists, offline or persisted caches, or when you want its Devtools and broader framework support beyond React. Both are mature and well maintained, and the patterns you learn in one transfer easily to the other.