React 2 min read 775 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.

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!

Related Articles

Related Articles

Need Help With Your Project?

Book a free 30-minute consultation to discuss your technical challenges and explore solutions together.