KS
Killer-Skills

tanstack-query — Categories.community

v1.0.0
GitHub

About this Skill

Perfect for Frontend Agents needing standardized data fetching with React Query and TanStack Query Example Turbo Repo with shadcn & next.js

jhs88 jhs88
[0]
[0]
Updated: 3/4/2026

Quality Score

Top 5%
28
Excellent
Based on code quality & docs
Installation
SYS Universal Install (Auto-Detect)
Cursor IDE Windsurf IDE VS Code IDE
> npx killer-skills add jhs88/my-shadcn-app-monorepo/tanstack-query

Agent Capability Analysis

The tanstack-query MCP Server by jhs88 is an open-source Categories.community integration for Claude and other AI agents, enabling seamless task automation and capability expansion.

Ideal Agent Persona

Perfect for Frontend Agents needing standardized data fetching with React Query and TanStack Query

Core Value

Empowers agents to manage data fetching with useSuspenseQuery, providing a standardized way to handle queries using queryKey and queryFn, and integrating seamlessly with libraries like next.js

Capabilities Granted for tanstack-query MCP Server

Standardizing data fetching across frontend applications
Implementing suspenseful data loading with useSuspenseQuery
Optimizing data fetching with queryKey and queryFn

! Prerequisites & Limits

  • Requires React and TanStack Query setup
  • Limited to frontend applications using React Query
Project
SKILL.md
12.2 KB
.cursorrules
1.2 KB
package.json
240 B
Ready
UTF-8

# Tags

[No tags]
SKILL.md
Readonly

The skill is for consistently using useSuspenseQuery from TanStack Query (React Query) in a standardized way across all frontend applications. Here's the pattern observed:

Basic Usage Pattern

javascript
1const { data } = useSuspenseQuery({ 2 queryKey: ["query-key", parameter1, parameter2], 3 queryFn: () => fetchFunction(parameter1, parameter2), 4});

Common Examples from the Codebase

  1. Simple data fetching:
javascript
1const { data: bids } = useSuspenseQuery({ 2 queryKey: ["bids", filters, type], 3 queryFn: () => fetchBids(filters, type), 4});
  1. Specific data:
javascript
1const { data } = useSuspenseQuery({ 2 queryKey: ["key", id], 3 queryFn: () => fetchData(id), 4});

Key Characteristics Observed

  1. Consistent naming: The hook result is destructured with data as the variable name (e.g., const { data: w9Data } = useSuspenseQuery(...))
  2. Query key patterns:
    • Simple strings for static queries: ["vendors"]
    • Parameterized keys for dynamic queries: ["vendor", vendorId]
    • Complex keys with multiple parameters: ["bids", filters, type]
  3. Integration with React Query utilities:
    • Often used alongside getQueryClient for cache invalidation on both server and client-side
    • Combined with useMutation for CRUD operations
    • Used in conjunction with error handling and loading states
  4. Error handling:
    • Some components check for errors explicitly (like in flh-traffic-module-frontend)
    • Often combined with toast notifications for user feedback
  5. Testing patterns:
    • Mocked in test files using vi.fn() to simulate data fetching
    • Tests typically mock the query key and return expected data structures

Common Use Cases

  1. Loading data on page load: Used in component initialization to fetch required data
  2. Content-specific information: Fetching data for specific records.
  3. Table/list data: Loading data for dynamic grids and tables

Best Practices Observed

Looking at the existing codebase, you should also ensure that:

  1. Query keys are consistently structured with a predictable pattern that includes the resource type and identifiers
  2. Error handling is implemented where appropriate, particularly in traffic/FLH modules
  3. Integration with mutations is common - query invalidation happens after mutations to keep data fresh
  4. The query is properly prefetched on the server-side in the page component (as shown in page.tsx files):
javascript
1void queryClient.prefetchQuery({ 2 queryKey: ["resource", id], 3 queryFn: () => fetchResource(id), 4});
  1. Use the same query key pattern that's already established in the codebase:
    • ["resource", id] for resource data
    • This maintains consistency with how other queries are handled

Key Points to Remember

  1. Always use proper import: Import getQueryClient() from "~/app/get-query-client" over useSuspenseQuery from "@tanstack/react-query".
  2. Use consistent query keys: Follow the pattern ["resource", id]
  3. Error handling: The suspense query will automatically handle loading states and errors
  4. Type safety: Make sure your action function returns proper data types that match your expectations
  5. Integration with existing code: The modules already have a pattern of using this approach, so maintain consistency

useMutation Pattern

This project uses a standardized pattern for data mutations (create, update, delete operations) with TanStack Query. The pattern ensures consistent error handling, loading states, and cache management.

Basic Mutation Structure

javascript
1const { mutate: actionMutate, isPending: isActionPending } = useMutation({ 2 mutationFn: (params) => actionFunction(params), 3 onSuccess: async () => { 4 // Success handling 5 toast.success("Success message"); 6 7 // Refresh related queries 8 await queryClient.invalidateQueries({ 9 queryKey: ["query-key", parameter], 10 }); 11 12 // Update UI state and navigate 13 setLocalState(updatedValue); 14 closeModal(); 15 router.push(`/path/${parameter}`); 16 }, 17 onError: (error) => { 18 // Error handling 19 console.error("Error:", error); 20 toast.error( 21 error instanceof Error ? error.message : "Generic error message", 22 ); 23 router.refresh(); 24 }, 25 onSettled: () => { 26 // Cleanup 27 form.reset(); 28 }, 29});

Key Principles

  1. Consistent Naming:

    • Mutation functions follow actionMutate pattern
    • Loading states use descriptive names like isCreating, isUpdating
    • Destructure from useMutation for clarity
  2. Error Handling:

    • Always check error instanceof Error before accessing properties
    • Provide generic fallback messages
    • Log errors and show user-friendly notifications
  3. Success Flow:

    • Toast notifications for immediate feedback
    • Query invalidation to refresh data
    • Local state updates as needed
    • Navigation or modal management
  4. Loading States:

    • Individual states per mutation
    • Combine states for complex operations
    • Disable UI elements during mutations

Common Patterns

Form Integration

javascript
1<form 2 onSubmit={(e) => { 3 e.preventDefault(); 4 mutationMutate({ param1: value1, param2: value2 }); 5 }} 6> 7 {/* Form fields */} 8 <SubmitButton disabled={isPending}>Submit</SubmitButton> 9</form>

Query Invalidation

javascript
1// Single query 2await queryClient.invalidateQueries({ 3 queryKey: ["resource", id], 4}); 5 6// Multiple related queries 7await queryClient.invalidateQueries({ queryKey: ["resource", id] }); 8await queryClient.invalidateQueries({ queryKey: ["related-data", id] });

Combined Loading States

javascript
1const isProcessing = isCreating || isUpdating || isDeleting;

Best Practices

  1. Type Safety: Use TypeScript interfaces for mutation parameters
  2. Server Actions: Use "use server" directive for mutation functions
  3. Consistent Messaging: Follow predictable error message patterns
  4. Loading Feedback: Disable buttons and show indicators during mutations
  5. Form Cleanup: Use onSettled for form resets and cleanup
  6. Navigation: Handle redirects appropriately after mutations

Custom Hooks (Optional)

For reusable mutations, extract to custom hooks:

javascript
1export const useCreateResource = (onSuccess?: () => void) => 2 useMutation({ 3 mutationFn: createResource, 4 onSuccess: (data) => { 5 toast.success("Successfully created resource."); 6 onSuccess?.(); 7 }, 8 onError: (error: Error) => { 9 toast.error(error.message || "Failed to create resource"); 10 }, 11 });

Key Characteristics

  1. Consistent Naming:

    • Mutation function: actionMutate (e.g., createContactMutate, updateContactMutate)
    • Loading state: isActionPending (e.g., isCreating, isUpdating)
    • Destructured from useMutation for clarity
  2. Error Handling:

    • Always check error instanceof Error before accessing error.message
    • Provide generic fallback messages
    • Log errors to console for debugging
    • Show user-friendly toast notifications
  3. Success Flow:

    • Toast notifications for immediate feedback
    • Query invalidation to refresh related data
    • Local state updates when needed
    • Navigation or modal closing
  4. Loading States:

    • Individual loading states per mutation
    • Combined loading states for complex operations: const isPending = isCreating || isUpdating || isRemoving;
    • UI elements disabled during mutations

Common Examples from Codebase

Create Mutation (VendorContacts.tsx)

javascript
1const { mutate: createContactMutate, isPending: isCreating } = useMutation({ 2 mutationFn: (data: ContactFormValues) => 3 addVendorContact(vendorId, { 4 name: data.name, 5 role: data.role, 6 phone: data.phone || "", 7 cellNumber: data.cellNumber || "", 8 email: data.email, 9 isPrimary: data.isPrimary ?? false, 10 }), 11 onSuccess: async () => { 12 await queryClient.invalidateQueries({ 13 queryKey: ["vendor-contacts", vendorId], 14 }); 15 const primaryContactCount = await countPrimaryContacts(vendorId); 16 setHasPrimaryVendorContact(primaryContactCount === 1); 17 setFormModalState(false); 18 toast.success("Successfully added vendor contact!"); 19 router.push(`/vendors/${vendorId}`); 20 }, 21 onError: (error) => { 22 console.error("Error updating contact:", error); 23 toast.error( 24 error instanceof Error ? error.message : "Error saving contact!", 25 ); 26 router.refresh(); 27 }, 28 onSettled: () => form.reset(), 29});

Delete Mutation Pattern

javascript
1const { mutate: removeContactMutate, isPending: isRemoving } = useMutation({ 2 mutationFn: async (contactId: string) => { 3 await removeVendorContact(contactId); 4 return contactId; 5 }, 6 onSuccess: async (contactId: string) => { 7 toast.success("Successfully removed vendor contact!"); 8 9 await queryClient.invalidateQueries({ 10 queryKey: ["vendor-contacts", vendorId], 11 }); 12 13 const primaryContactCount = await countPrimaryContacts(vendorId); 14 setHasPrimaryVendorContact(primaryContactCount === 1); 15 router.push(`/vendors/${vendorId}`); 16 }, 17 onError: (error) => { 18 console.error("Error removing contact:", error); 19 toast.error( 20 error instanceof Error ? error.message : "Failed to remove contact", 21 ); 22 router.refresh(); 23 }, 24 onSettled: () => setRemoveModalState(false), 25});

Form Integration Pattern

javascript
1<form 2 onSubmit={(e) => { 3 e.preventDefault(); 4 mutationMutate({ param1: value1, param2: value2 }); 5 }} 6> 7 <Field> 8 <FieldContent> 9 <Textarea 10 value={notes} 11 onChange={(e) => setNotes(e.target.value)} 12 required 13 placeholder="Enter notes..." 14 /> 15 <DialogFooter> 16 <DialogClose asChild> 17 <Button variant="outline">Close</Button> 18 </DialogClose> 19 <SubmitButton variant="destructive" disabled={isPending}> 20 Submit Action 21 </SubmitButton> 22 </DialogFooter> 23 </FieldContent> 24 </Field> 25</form>

Query Invalidation Patterns

  1. Targeted Invalidation: Always use specific query keys

    javascript
    1await queryClient.invalidateQueries({ 2 queryKey: ["vendor-documents", vendorId], 3});
  2. Multiple Related Queries: When mutations affect multiple data types

    javascript
    1await queryClient.invalidateQueries({ 2 queryKey: ["vendor-documents", vendorId], 3}); 4await queryClient.invalidateQueries({ queryKey: ["audit-logs", vendorId] }); 5await queryClient.invalidateQueries({ 6 queryKey: ["vendor-document-counts", vendorId], 7});
  3. Sequential Operations: Perform invalidations in order, sometimes with additional async operations

Best Practices

  1. Type Safety: Define proper TypeScript interfaces for mutation parameters
  2. Server Actions: All mutation functions should be server actions with "use server" directive
  3. Consistent Error Messages: Use predictable error message patterns
  4. Loading State Management: Disable buttons and show loading indicators during mutations
  5. Form Reset: Use onSettled callback to clean up form state
  6. Navigation: Use router.push() or router.refresh() appropriately after mutations

Custom Hook Pattern (Advanced)

For reusable mutations, create custom hooks:

javascript
1export const useCreateBid = (onSuccess?: () => void) => 2 useMutation({ 3 mutationFn: createBid, 4 onSuccess: (data) => { 5 toast.success("Successfully created bid."); 6 onSuccess?.(); 7 }, 8 onError: (error: Error) => { 9 toast.error(error.message || "Failed to create bid", { 10 duration: Infinity, 11 action: { 12 label: "Dismiss", 13 onClick: () => console.log("Error was dismissed"), 14 }, 15 }); 16 }, 17 });

Summary

The implementation in the modules follows a consistent pattern where:

  • Server-side prefetching is used to pre-load queries
  • Client-side getQueryClient or useSuspenseQuery is used for components that need to fetch data
  • useMutation follows standardized patterns for all CRUD operations
  • Query keys follow a predictable naming convention
  • The same fetch action function is used consistently

This approach ensures proper hydration, loading states, consistent caching behavior, and maintainable mutation logic across the application.

Related Skills

Looking for an alternative to tanstack-query or building a Categories.community AI Agent? Explore these related open-source MCP Servers.

View All

widget-generator

Logo of f
f

widget-generator is an open-source AI agent skill for creating widget plugins that are injected into prompt feeds on prompts.chat. It supports two rendering modes: standard prompt widgets using default PromptCard styling and custom render widgets built as full React components.

149.6k
0
Design

chat-sdk

Logo of lobehub
lobehub

chat-sdk is a unified TypeScript SDK for building chat bots across multiple platforms, providing a single interface for deploying bot logic.

73.0k
0
Communication

zustand

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication

data-fetching

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication