TrueSpec

Data Fetching with TanStack Query

Queries, mutations, pagination, infinite scroll, and optimistic updates

What You’ll Build

After following this guide, you will have a working implementation of data fetching with tanstack query in your project. TanStack Query (React Query) is the best way to fetch, cache, and synchronize server data in React. Eliminates loading/error state boilerplate, deduplicates requests, handles background refetching, and provides cache invalidation. No more useEffect + useState for API calls.

Use Cases & Problems Solved

  • Implement interactive UI features that users expect from modern apps
  • Follow established patterns that scale and remain maintainable
  • Reduce boilerplate and avoid common frontend pitfalls

Prerequisites

  • React 18+

Step-by-Step Implementation

Install and setup provider

The following snippet shows how to install and setup provider. Copy this into your project and adjust the values for your environment.

npm install @tanstack/react-query

Queries and mutations

The following snippet shows how to queries and mutations. Copy this into your project and adjust the values for your environment.

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

// Fetch data with automatic caching and refetching
function UserList() {
  const { data: users, isLoading, error } = useQuery({
    queryKey: ['users'],
    queryFn: () => fetch('/api/users').then(r => r.json()),
    staleTime: 5 * 60 * 1000, // Consider fresh for 5 minutes
  });

  const queryClient = useQueryClient();
  const createUser = useMutation({
    mutationFn: (newUser) => fetch('/api/users', {
      method: 'POST', body: JSON.stringify(newUser),
      headers: { 'Content-Type': 'application/json' },
    }).then(r => r.json()),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] }); // Refetch list
    },
  });

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

  return (
    <div>
      {users.map(u => <p key={u.id}>{u.name}</p>)}
      <button onClick={() => createUser.mutate({ name: 'New User' })}>
        Add User
      </button>
    </div>
  );
}

⚠️ Don’t Do This

❌ Using useEffect + useState for data fetching

const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
  fetch('/api/users')
    .then(r => r.json())
    .then(setData)
    .catch(setError)
    .finally(() => setLoading(false));
}, []);
// No caching, no refetch, no deduplication, race conditions!

✅ Use TanStack Query — handles all edge cases automatically

const { data, isLoading, error } = useQuery({
  queryKey: ['users'],
  queryFn: () => fetch('/api/users').then(r => r.json()),
});
// Cached, deduped, auto-refetches, handles race conditions!

Testing

Verify your implementation with these tests:

// __tests__/data-fetching-with-tanstack-query.test.ts
import { describe, it, expect } from 'vitest';

describe('Data Fetching with TanStack Query', () => {
  it('should initialize without errors', () => {
    // Test that the setup completes successfully
    expect(() => setup()).not.toThrow();
  });

  it('should handle the primary use case', async () => {
    const result = await execute();
    expect(result).toBeDefined();
    expect(result.success).toBe(true);
  });

  it('should handle edge cases', async () => {
    // Test with empty/null input
    const result = await execute(null);
    expect(result.error).toBeDefined();
  });
});

Verification

# Open React DevTools → install TanStack Query Devtools
# npm install @tanstack/react-query-devtools
# Open devtools panel → see all query cache entries
# Navigate away and back — data loads instantly from cache

Related Specs

Advanced

Drag & Drop Kanban Board

dnd-kit drag and drop, column reorder, and state persistence

Frontend Patterns
Beginner

Zustand State Management

Store setup, slices, persist middleware, devtools, and TypeScript

Frontend Patterns
Beginner

React Form Validation (React Hook Form + Zod)

Type-safe forms, custom validators, error display, and submission handling

Frontend Patterns