We use cookies to understand how the site is used and to display ads. Analytics and advertising only run after you accept. You can change your choice anytime. Privacy policy

Skip to content
devvkit
$devvkit learn --librarie tanstack-query-(react-query)-guide

TanStack Query (React Query) Guide

[react][data-fetching][caching]
JavaScript / TypeScript
Install
npm install @tanstack/react-query

TanStack Query solves keeping server state in sync with the UI. It handles caching, background refetching, and optimistic updates.

Server state is fundamentally different from client state. TanStack Query treats server state as a cache, not source of truth.

v5 uses useQuery for reads, useMutation for writes, and queryClient.invalidateQueries to refetch.

Setup

Provider· Wrap app with QueryClientProvider.
const queryClient = new QueryClient();
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>

Queries

Basic query· Fetch data.
const { data, isLoading } = useQuery({
  queryKey: ['users'],
  queryFn: () => fetch('/api/users').then(r => r.json()),
});
Query with params· Depend on variable.
const { data } = useQuery({
  queryKey: ['users', userId],
  queryFn: () => fetch('/api/users/' + userId).then(r => r.json()),
});
Enabled· Skip until condition.
useQuery({ queryKey: ['users', userId], queryFn: fetchUser, enabled: !!userId });

Mutations

Mutation· Create and invalidate.
const mutation = useMutation({
  mutationFn: (newUser) => fetch('/api/users', { method: 'POST', body: JSON.stringify(newUser) }),
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
});
Optimistic update· Update UI before server.
useMutation({
  mutationFn: updateTodo,
  onMutate: async (newTodo) => {
    await queryClient.cancelQueries({ queryKey: ['todos'] });
    const prev = queryClient.getQueryData(['todos']);
    queryClient.setQueryData(['todos'], (old) => [...old, newTodo]);
    return { prev };
  },
  onError: (err, newTodo, context) => queryClient.setQueryData(['todos'], context.prev),
  onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
});

Caching

Stale time· Control freshness duration.
useQuery({ queryKey: ['users'], queryFn: fetchUsers, staleTime: 5 * 60 * 1000, gcTime: 30 * 60 * 1000 });

Pagination

Infinite query· Load more pattern.
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
  queryKey: ['projects'],
  queryFn: ({ pageParam }) => fetch(/api/projects?cursor=).then(r => r.json()),
  initialPageParam: 0,
  getNextPageParam: (lastPage) => lastPage.nextCursor ?? null,
});