React 2 min read 710 views

How to Use React Query for Server State Management in 2026

Master data fetching, caching, and synchronization with React Query (TanStack Query) in your React apps.

E
React Query data fetching

Installation

npm install @tanstack/react-query

Setup Provider

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient({
    defaultOptions: {
        queries: {
            staleTime: 5 * 60 * 1000, // 5 minutes
            retry: 1,
        },
    },
});

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

Basic Query

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

function Users() {
    const { data, isLoading, error } = useQuery({
        queryKey: ['users'],
        queryFn: async () => {
            const res = await fetch('/api/users');
            return res.json();
        },
    });

    if (isLoading) return <p>Loading...</p>;
    if (error) return <p>Error: {error.message}</p>;

    return (
        <ul>
            {data.map(user => (
                <li key={user.id}>{user.name}</li>
            ))}
        </ul>
    );
}

Mutations

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

function CreateUser() {
    const queryClient = useQueryClient();

    const mutation = useMutation({
        mutationFn: (newUser) => {
            return fetch('/api/users', {
                method: 'POST',
                body: JSON.stringify(newUser),
            });
        },
        onSuccess: () => {
            // Invalidate and refetch
            queryClient.invalidateQueries({ queryKey: ['users'] });
        },
    });

    return (
        <button
            onClick={() => mutation.mutate({ name: 'New User' })}
            disabled={mutation.isPending}
        >
            {mutation.isPending ? 'Creating...' : 'Create User'}
        </button>
    );
}

Optimistic Updates

const mutation = useMutation({
    mutationFn: updateTodo,
    onMutate: async (newTodo) => {
        await queryClient.cancelQueries({ queryKey: ['todos'] });

        const previousTodos = queryClient.getQueryData(['todos']);

        queryClient.setQueryData(['todos'], (old) =>
            old.map(t => t.id === newTodo.id ? newTodo : t)
        );

        return { previousTodos };
    },
    onError: (err, newTodo, context) => {
        queryClient.setQueryData(['todos'], context.previousTodos);
    },
    onSettled: () => {
        queryClient.invalidateQueries({ queryKey: ['todos'] });
    },
});
Share this article:
ES

Written by Edrees Salih

Full-stack software engineer with 9 years of experience. Passionate about building scalable solutions and sharing knowledge with the developer community.

View Profile

Comments (0)

Leave a Comment

Your email will not be published.

No comments yet. Be the first to share your thoughts!