Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
acedergren avatar

Tanstack Query

  • 22 installs
  • 22 repo stars
  • Updated May 28, 2026
  • acedergren/agentic-tools

tanstack-query is a Claude Code skill for troubleshooting TanStack Query (React Query) v5 issues such as v4-to-v5 migration errors, refetch loops, and SSR hydration mismatches.

About

This skill is expert troubleshooting for TanStack Query (React Query) v5. It covers v4-to-v5 migration gotchas like gcTime, isPending and throwOnError, infinite refetch loops, SSR hydration mismatches, and when to use React Query versus SWR or Zustand. A developer uses it when production data-fetching breaks, not for basic useQuery setup. It stresses that React Query is a server cache, not a general state manager.

  • Troubleshoots TanStack Query v5 issues: v4 to v5 migration, refetch loops, SSR hydration
  • Decision tree for when NOT to use React Query (URL, derived, form, realtime state)
  • staleTime selection table by data update frequency

Tanstack Query by the numbers

  • 22 all-time installs (skills.sh)
  • Ranked #1,528 of 2,245 Frontend Development skills by installs in the Skillselion catalog
  • Data as of Jul 28, 2026 (Skillselion catalog sync)
At a glance

tanstack-query capabilities & compatibility

Capabilities
data fetching · cache tuning · ssr debugging · migration
Use cases
frontend · debugging · refactoring
IDEs
vscode · cursor ide
Pricing
Free
From the docs

What tanstack-query says it does

**The trap**: Developers use React Query for everything. It's a **server cache**, not a state manager.
SKILL.md
**Failure mode**: Silent — code runs, TypeScript doesn't error, cache garbage-collects immediately.
SKILL.md
npx skills add https://github.com/acedergren/agentic-tools --skill tanstack-query

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs22
repo stars22
Last updatedMay 28, 2026
Repositoryacedergren/agentic-tools

What it does

Debug TanStack Query v5 migration errors, refetch loops, and SSR hydration mismatches.

Who is it for?

Debugging TanStack Query v5 migration gotchas, infinite refetch loops, and SSR hydration mismatches.

Skip if: Basic useQuery setup, which the skill explicitly excludes.

When should I use this skill?

When debugging TanStack Query v4 to v5 migration errors, refetch loops, or SSR hydration mismatches.

What you get

Query bugs are traced to the right v5 API, staleTime is chosen by update frequency, and React Query is scoped to server state.

By the numbers

  • 5-row staleTime selection table
  • Multiple documented v4 to v5 breaking changes

Files

SKILL.mdMarkdownGitHub ↗

TanStack Query v5 - Expert Troubleshooting

Assumption: You know useQuery basics. This covers what breaks in production.

Arguments

  • $ARGUMENTS: Query bug, migration issue, or caching decision to analyze
  • Example: /tanstack-query infinite refetch loop on dashboard
  • Example: /tanstack-query v4 to v5 cacheTime issue
  • If empty: ask which TanStack Query issue is in scope

---

Before Using React Query: Strategic Assessment

When NOT to Use React Query

Need data fetching?
│
├─ Data from URL (search params, path) → DON'T use queries
│   └─ Use framework loaders (Next.js, Remix)
│      WHY: Queries cache by key, URL is already your cache key
│
├─ Derived/computed data → DON'T use queries
│   └─ Use useMemo or Zustand
│      WHY: No server, no stale data, no refetch needed
│
├─ Form state → DON'T use queries
│   └─ Use React Hook Form or controlled state
│
├─ WebSocket/realtime (> 1/sec) → DON'T use queries
│   └─ Use Zustand; queries are designed for request/response, not streaming
│
└─ REST/GraphQL server state → USE queries ✅

The trap: Developers use React Query for everything. It's a server cache, not a state manager.

staleTime Selection

Update frequencyRecommended staleTime
Real-time (>1/sec)WebSocket + Zustand instead
Frequent (<1/min)30s–1min
Moderate (5–30min)5min (default)
Infrequent (>1hr)30min+
Critical (money, auth)0 (always fresh)

---

Breaking Changes: v4 → v5 Migration Gotchas

❌ #1: cacheTime Renamed to gcTime

Failure mode: Silent — code runs, TypeScript doesn't error, cache garbage-collects immediately.

// WRONG - silently ignored in v5
useQuery({ queryKey: ['todos'], queryFn: fetchTodos, cacheTime: 10 * 60 * 1000 })

// CORRECT
useQuery({ queryKey: ['todos'], queryFn: fetchTodos, gcTime: 10 * 60 * 1000 })

Debug signal: DevTools shows 0ms gcTime despite setting 10 minutes.

❌ #2: isLoading Removed → Use isPending

Failure mode: if (isLoading) evaluates falsy (undefined), spinner never shows.

// WRONG - isLoading is undefined in v5
const { isLoading } = useQuery(...)

// CORRECT
const { isPending } = useQuery(...)

Semantic difference: isPending stays true during refetches with cached data — isLoading did not. Causes "stale data + spinner simultaneously" if naively swapped.

❌ #3: keepPreviousDataplaceholderData

Failure mode: Pagination flickers on page change.

// WRONG
useQuery({ queryKey: ['todos', page], keepPreviousData: true })

// CORRECT - function form required
useQuery({
  queryKey: ['todos', page],
  placeholderData: (previousData) => previousData,
})

❌ #4: Query Functions Must Return Non-Void

Failure mode: Silent runtime error when using any types.

// WRONG - void return
queryFn: async () => { await api.deleteTodo(id) }

// CORRECT
queryFn: async () => { await api.deleteTodo(id); return { success: true } }

---

Performance Pitfalls

❌ Infinite Refetch Loop

Cause: Object or array reference in queryKey — new reference on every render triggers new query.

// WRONG - object in key = new reference each render = infinite loop
useQuery({ queryKey: ['user', user], queryFn: () => fetchUser(user.id) })

// CORRECT - use stable primitives
useQuery({ queryKey: ['user', user.id], queryFn: () => fetchUser(user.id) })

Detection: Network tab shows identical requests >10/sec. React DevTools Profiler shows constant re-renders.

Fallback (when key must contain object):

const stableKey = useMemo(() => ['user', user], [user.id])
useQuery({ queryKey: stableKey, queryFn: () => fetchUser(user.id), structuralSharing: false })

❌ Stale Data Trap

Cause: staleTime: Infinity — data never marked stale regardless of server changes.

Detection: Network tab shows zero requests after initial load. Users report "data doesn't update" but devs can't reproduce (devs refresh frequently, clearing cache).

Fix: Use reasonable staleTime. If still stale: queryClient.invalidateQueries({ queryKey: ['your-key'] }).

❌ Over-Invalidation

Cause: queryClient.invalidateQueries() with no filter nukes entire cache → all queries refetch.

// WRONG - refetches 100 queries on every mutation
onSuccess: () => { queryClient.invalidateQueries() }

// CORRECT - targeted
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['user', userId] }) }

---

Decision Frameworks

Optimistic Updates vs Invalidation

Mutation completes...
│
├─ Simple list append/prepend → Optimistic (useMutationState)
│   └─ Add todo, add comment — no complex logic needed
│
├─ Complex computed data → Invalidation
│   └─ Aggregates, filters, sorts — let server compute
│
├─ Risk of conflicts (multi-user) → Invalidation
│   └─ Optimistic update may be wrong; let server resolve
│
└─ Must feel instant → Optimistic + rollback on error
    └─ Toggle like, toggle favorite

React Query vs SWR

Prefer React QueryPrefer SWR
Fine-grained gc/stale controlSimpler API (less config)
Complex invalidation patternsSmaller bundle size priority
Optimistic updates with rollbackNext.js (first-party support)
Infinite queries / paginationSimple dashboard use case
Already in TanStack ecosystem

---

SSR Hydration (Next.js App Router)

❌ Mismatch Pattern

Server renders "Loading...", client has cached data → hydration error.

✅ Prefetch Pattern

// app/page.tsx (Server Component)
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'

export default async function Page() {
  const queryClient = new QueryClient()
  await queryClient.prefetchQuery({ queryKey: ['todos'], queryFn: fetchTodos })
  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <TodoList />
    </HydrationBoundary>
  )
}

// components/TodoList.tsx ('use client')
export function TodoList() {
  const { data } = useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
  // No isPending check — data guaranteed from server prefetch
  return <div>{data.map(...)}</div>
}

Hydration mismatch fallback: Pass as initialData via props instead of prefetch.

---

Debugging Commands

// Find refetch loops — add to QueryClient defaultOptions
onSuccess: (data, query) => { console.count(`Refetch: ${query.queryKey}`) }
// Count > 10 in 1 second = infinite loop

// Check cache state
const state = queryClient.getQueryState(['todos'])
console.log(state?.isInvalidated)

// Nuclear cache clear
queryClient.removeQueries({ queryKey: ['your-key'] })
queryClient.refetchQueries({ queryKey: ['your-key'] })
// Or: queryClient.clear()
# Find v4 property names still in codebase
grep -r "cacheTime\|isLoading\|keepPreviousData" src/

Add <ReactQueryDevtools initialIsOpen={false} /> to visualize cache state, refetch counts, and staleness.

---

When to Load Full Reference

READ `references/v5-features.md` when using 3+ v5-specific features simultaneously (useMutationState, throwOnError, infinite queries, suspense mode).

READ `references/migration-guide.md` when migrating a codebase with 10+ query usages or running codemods.

Do NOT load references for single breaking change fixes, basic troubleshooting, or simple optimistic updates — all covered above.

---

Resources

  • Official Docs: https://tanstack.com/query/latest

Related skills

FAQ

Why did my cache garbage-collect immediately after upgrading to v5?

cacheTime was renamed to gcTime in v5; the old key is silently ignored, so gcTime defaults to 0.

Is React Query a state manager?

No; it is a server cache, not a state manager, and should not be used for URL, derived, form, or realtime state.

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.