Skip to content

TanStack Query (React Query)

TanStack Query (formerly React Query) manages server state: caching, background refetch, deduplication, pagination, and mutations. It eliminates most manual fetch + useEffect + useState boilerplate.

Queries and Mutations

useQuery fetches and caches data keyed by queryKey. Identical keys share cache across components. useMutation handles POST/PUT/DELETE with success/error callbacks and cache invalidation.

Install @tanstack/react-query and wrap app in QueryClientProvider. DevTools optional but invaluable during development.

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <RouterProvider router={router} />
    </QueryClientProvider>
  );
}

function Posts() {
  const { data, isLoading } = useQuery({
    queryKey: ['posts'],
    queryFn: () => fetch('/api/posts').then(r => r.json()),
  });
}

queryKey uniquely identifies cached data — include all variables that affect the fetch.

Core TanStack Query APIs

useQuery({ queryKey, queryFn, staleTime, enabled })
useMutation({ mutationFn, onSuccess })
queryClient.invalidateQueries({ queryKey: ['posts'] })
  • queryKey arrays should include filter params: ['posts', { page }].
  • staleTime controls how long data is considered fresh.
  • enabled: false skips fetch until condition true.
  • Invalidate queries after mutations to refresh lists.

TanStack Query Reference

Essential APIs for React server state.

API Purpose
useQuery Fetch + cache read
useMutation Create/update/delete
queryKey Cache identifier
invalidateQueries Mark stale, refetch
setQueryData Optimistic cache update
prefetchQuery Load before navigation

Designing Query Keys

Hierarchical keys enable targeted invalidation: ['posts'] invalidates all post queries; ['posts', id] targets one record.

['posts']                    // all posts
['posts', { status: 'draft' }] // filtered
['posts', postId]              // single post

Mutations and Cache Updates

After creating a post, invalidate ['posts'] or optimistically append to cache with setQueryData for instant UI.

const mutation = useMutation({
  mutationFn: createPost,
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ['posts'] }),
});

Common Mistakes

  • Unstable query keys causing infinite refetches.
  • Storing server data in Redux/Zustand unnecessarily.
  • Not invalidating cache after mutations.
  • Missing QueryClientProvider above consumers.

Key Takeaways

  • TanStack Query owns server/async state and caching.
  • useQuery + queryKey replaces most fetch effects.
  • useMutation + invalidation keeps UI in sync after writes.
  • Design hierarchical query keys for flexible cache control.

Pro Tip

Install @tanstack/react-query-devtools during development — visualize cache state and query lifecycles instantly.