bobmatnyc/claude-mpm-skills2 files

Tanstack Query

TanStack Query (React Query) for asynchronous server-state management with automatic caching, background refetching, optimistic updates, and pagination in React applications.

Specification
Skill ID
bobmatnyc/claude-mpm-skills/tanstack-query
Publisher
bobmatnyc
Repository
claude-mpm-skills
Installs
0
Files
2
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

bobmatnyc/claude-mpm-skills/tanstack-queryInstalls these files
  • SKILL.md
  • metadata.json

What this skill tells the agent

TanStack Query (React Query) Skill

Summary

TanStack Query (formerly React Query) is a powerful asynchronous state management library for React that handles server-state fetching, caching, synchronization, and updates. It eliminates the need for manual data fetching boilerplate and provides built-in features like background refetching, optimistic updates, pagination, and intelligent cache management.

When to Use

Use TanStack Query when:

  • Fetching data from REST APIs, GraphQL, or tRPC endpoints
  • Need automatic background refetching and cache invalidation
  • Building real-time dashboards with polling or websocket data
  • Implementing infinite scroll or pagination
  • Require optimistic UI updates for mutations
  • Managing complex server-state synchronization
  • Need offline support with cache persistence
  • Building applications with frequent data updates

TanStack Query excels at:

  • Server-state management (API data, external state)
  • Request deduplication and caching
  • Stale-while-revalidate patterns
  • Loading and error state management
  • Prefetching and eager loading
  • Parallel and dependent query orchestration

Avoid TanStack Query for:

  • Pure client-side state (use Zustand, Jotai, or Context)
  • Form state management (use React Hook Form, Formik)
  • Simple one-time fetches without caching needs

Quick Start

Installation

npm install @tanstack/react-query
# DevTools (optional but recommended)
npm install @tanstack/react-query-devtools

Basic Setup

// app/providers.tsx
'use client';

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

export function Providers({ children }: { children: React.ReactNode }) {
  const [queryClient] = useState(() => new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 60 * 1000, // 1 minute
        refetchOnWindowFocus: false,
      },
    },
  }));

  return (
    <QueryClientProvider client={queryClient}>
      {children}
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}

First Query

// components/UserProfile.tsx
import { useQuery } from '@tanstack/react-query';

interface User {
  id: number;
  name: string;
  email: string;
}

function UserProfile({ userId }: { userId: number }) {
  const { data, isLoading, error } = useQuery({
    queryKey: ['user', userId],
    queryFn: async () => {
      const response = await fetch(`/api/users/${userId}`);
      if (!response.ok) throw new Error('Failed to fetch user');
      return response.json() as Promise<User>;
    },
  });

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

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.email}</p>
    </div>
  );
}

First Mutation

// components/CreateUserForm.tsx
import { useMutation, useQueryClient } from '@tanstack/react-query';

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

  const mutation = useMutation({
    mutationFn: async (newUser: { name: string; email: string }) => {
      const response = await fetch('/api/users', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(newUser),
      });
      return response.json();
    },
    onSuccess: () => {
      // Invalidate and refetch users list
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    mutation.mutate({
      name: formData.get('name') as string,
      email: formData.get('email') as string,
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" placeholder="Name" required />
      <input name="email" type="email" placeholder="Email" required />
      <button type="submit" disabled={mutation.isPending}>
        {mutation.isPending ? 'Creating...' : 'Create User'}
      </button>
      {mutation.isError && <p>Error: {mutation.error.message}</p>}
    </form>
  );
}

Core Concepts

Server State vs Client State

Server State Characteristics:

  • Persisted remotely (database, API, cloud)
  • Requires asynchronous APIs for fetching/updating
  • Can be out of sync with client
  • Can be updated by other users/systems
  • Examples: User data, posts, products, settings

Client State Characteristics:

  • Persisted locally (memory, localStorage)
  • Synchronously accessible
  • Fully controlled by client
  • Examples: UI theme, modal open/closed, form inputs

TanStack Query manages server state. Use Zustand/Context for client state.

Query Keys

Query keys uniquely identify queries and their cached data.

Key Structure:

// String key (simple)
queryKey: ['todos']

// Array key (recommended for dependencies)
queryKey: ['todo', todoId]
queryKey: ['todos', { status: 'active', page: 1 }]

// Nested arrays (complex hierarchies)
queryKey: ['users', userId, 'posts', { sort: 'date' }]

Key Matching:

// Exact match
queryClient.invalidateQueries({ queryKey: ['todos', 1], exact: true });

// Prefix match (invalidates all matching)
queryClient.invalidateQueries({ queryKey: ['todos'] }); // Matches ['todos', 1], ['todos', 2], etc.

// Predicate match
queryClient.invalidateQueries({
  predicate: (query) => query.queryKey[0] === 'todos' && query.state.data?.status === 'draft'
});

Best Practices:

  • Use arrays with hierarchical structure: ['resource', id, 'subresource']
  • Place variables at the end: ['users', { filter, sort }]
  • Consistent ordering across components
  • Use objects for complex parameters

Query Lifecycle

FRESH → STALE → INACTIVE → GARBAGE COLLECTED
  ↓       ↓         ↓              ↓
  0ms   staleTime  no observers  cacheTime

States:

  • Fresh: Data is considered up-to-date (within staleTime)
  • Stale: Data might be outdated, will refetch on trigger
  • Inactive: No components using the query
  • Garbage Collected: Removed from cache after cacheTime

Configuration:

useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
  staleTime: 5 * 60 * 1000,     // 5 minutes (data fresh)
  gcTime: 10 * 60 * 1000,       // 10 minutes (cache retention)
  refetchOnWindowFocus: true,    // Refetch when window regains focus
  refetchOnReconnect: true,      // Refetch when reconnecting
  refetchInterval: 30000,        // Poll every 30 seconds
});

Cache Behavior

Automatic Caching:

// First component - triggers fetch
function ComponentA() {
  const { data } = useQuery({ queryKey: ['user', 1], queryFn: fetchUser });
  return <div>{data?.name}</div>;
}

// Second component - uses cache instantly
function ComponentB() {
  const { data } = useQuery({ queryKey: ['user', 1], queryFn: fetchUser });
  return <div>{data?.email}</div>; // No second fetch!
}

Stale-While-Revalidate:

// Shows cached data immediately, refetches in background if stale
const { data, isRefetching } = useQuery({
  queryKey: ['posts'],
  queryFn: fetchPosts,
  staleTime: 60000, // Fresh for 1 minute
});

// data available from cache immediately
// isRefetching = true if background refetch happening

Queries

useQuery Hook

Basic Syntax:

const {
  data,           // Query result
  error,          // Error object if failed
  isLoading,      // First load (no cached data)
  isFetching,     // Any fetch (including background)
  isSuccess,      // Query succeeded
  isError,        // Query failed
  status,         // 'pending' | 'error' | 'success'
  fetchStatus,    // 'fetching' | 'paused' | 'idle'
  refetch,        // Manual refetch function
} = useQuery({
  queryKey: ['key'],
  queryFn: async () => { /* fetch logic */ },
});

Query Function Patterns

Basic Fetch:

Use this skill in a project

Open any RiverX project, search the Skills panel for bobmatnyc/claude-mpm-skills/tanstack-query, and install it in one click.

Use this skill