Yashveer Singh
Connect
<- All posts
Web App and Frontend Development6 min read

React Query vs SWR vs RTK Query

React Query, SWR, and RTK Query are React libraries for managing server state: data fetched from an API that needs to be cached, synchronized, and updated in the UI. They solve the problem of manual fetch-loading-error state management, providing automatic caching, background refetching, stale-while-revalidate semantics, optimistic updates, and pagination utilities. The distinction between client state (what the user has done) and server state (what the server has) is the conceptual foundation for all three libraries.

Written by Yashveer Singh, founder of Yashveer Labs.

What you need to know

  • React Query is the most capable of the three and the right default for new projects that do not already use Redux.
  • SWR is the right choice when bundle size is a constraint or when the data fetching requirements are straightforward.
  • RTK Query belongs in applications that already use Redux Toolkit. Adding Redux specifically for RTK Query is not worth it.
  • All three eliminate the need to manage loading, error, and cache state manually. Pick one and use it consistently rather than mixing libraries.
  • Server state (API data) and client state (UI state) are different problems. These libraries solve server state. useState and useContext are still appropriate for client state.

The core argument

The pattern of fetching data in useEffect, storing it in useState, and manually managing loading and error states is one of the most commonly reimplemented patterns in React applications. Every team writes it slightly differently, with different edge cases handled or missed. React Query, SWR, and RTK Query replace this manual pattern with a consistent, well-tested implementation that handles the edge cases correctly by default: request deduplication (two components mounting simultaneously that need the same data produce one request), background refetching when the window regains focus, stale-while-revalidate semantics that show cached data immediately while fetching fresh data, and automatic retry with exponential backoff on network errors.

The choice between the three is primarily about what else the application is doing. For a new Next.js application with no existing Redux, React Query is the strongest choice. The devtools are excellent, the documentation is comprehensive, and the feature set covers complex data fetching scenarios like dependent queries, optimistic updates, and infinite scroll without requiring library extensions. React Query v5 in particular cleaned up the API significantly and is the version to use for new projects.

For applications already invested in Redux Toolkit, RTK Query is a natural fit. The integration with Redux DevTools, the ability to see server state alongside client state, and the tag-based cache invalidation that maps naturally to REST API resource relationships make RTK Query the coherent choice when Redux is already in the dependency graph. I used RTK Query on Nyxera where we already had significant Redux state for the workflow editor, and the ability to see API cache state in Redux DevTools alongside the client state was genuinely useful for debugging.

Common mistakes

  1. Mixing React Query and custom useEffect data fetching in the same codebase. Inconsistent data fetching patterns make caching behavior unpredictable: some data is cached and deduplicated, other data is not. Migrate all data fetching to the chosen library and delete the custom useEffect patterns.
  1. Using the same query key for different data. React Query's cache is keyed by the query key array. If two different data sources use the same key, they share cache. Query keys should be specific: ['user', userId] not ['user']. Include all variables that affect the data in the query key.
  1. Not using the onSuccess and onError callbacks for side effects. Mutations that trigger notifications, navigate to a new page, or reset form state should use the mutation's onSuccess and onError callbacks rather than manual handling after awaiting the mutation. The callbacks receive the mutation result and are the correct place for side effects.
  1. Disabling React Query's background refetching without understanding the implications. staleTime and gcTime control how long data is considered fresh and how long it stays in cache. Setting staleTime to Infinity prevents all background refetching, which improves performance but means data shown to users may be outdated until they manually trigger a refresh. Set staleTime based on how frequently the data changes, not to maximize cache hits.
  1. Not invalidating queries after mutations. A mutation that creates or updates a resource should invalidate the queries that show that resource in a list or detail view. Without invalidation, the UI shows stale data after a successful mutation. React Query's queryClient.invalidateQueries makes this explicit. RTK Query's tag invalidation handles it automatically when endpoints are tagged correctly.

Where to start

  1. Install React Query v5 and wrap the application in QueryClientProvider. Add the React Query DevTools in development mode. The devtools show all active queries, their cache status, and their data, which is essential for understanding cache behavior during development.
  1. Convert one data-fetching useEffect to useQuery. Pick a simple read-only data fetch, convert it to a useQuery call, and verify that loading, error, and success states work as expected. Verify in the DevTools that the cache shows the data and that subsequent mounts of the same component use the cache instead of refetching.
  1. Convert one mutation to useMutation with query invalidation. Pick a create or update operation. Implement it with useMutation and add queryClient.invalidateQueries in the onSuccess callback for the queries that should update after the mutation. This establishes the full read-write pattern for the rest of the application.

Related reading

FAQ

Frequently asked

Author

Why you should hire Yashveer Singh for this

The kind of work this article describes is the kind of work I do every week. Production deployments, scaling decisions, the architecture choices that compound over years. I am Yashveer Singh, founder of Yashveer Labs. If you need this done, I do not need to be sold on the brief. Send me what you have and I will tell you what it actually takes.

Related reading