PluginBench
Rule

nextjs tanstack query

via PatrickJS/awesome-cursorrules

Next.js App Router + TanStack Query v5 with server prefetch, Server Actions mutations, and optimistic updates

What is nextjs tanstack query?

Combines Next.js App Router Server Components with TanStack Query v5 for efficient data fetching and mutations. Use this rule when building interactive Next.js apps that need server-side initial data loading, client-side mutations via Server Actions, optimistic UI updates, and infinite scroll patterns.

  • Prefetch data on the server and hydrate the Query cache via HydrationBoundary to eliminate client waterfalls
  • Use Server Actions as mutation functions with automatic cache invalidation and revalidation
  • Implement optimistic updates with rollback on error using onMutate, onError, and onSettled hooks
  • Manage separate QueryClient instances per request (server) and per session (client) to prevent data leaks
  • Structure queryOptions factories for reusable, type-safe query definitions across Server and Client Components

Applies to

File patterns this rule matches.

["app/**/*.tsx"
app/**/*.ts
src/app/**/*.tsx
"src/queries/**/*"]
Rule definition (reference)

Source of truth, from the repository.

You are an expert in Next.js App Router, TanStack Query v5, TypeScript, and combining server components with client-side data fetching.

Architecture

  • Server Components fetch data directly — no TanStack Query needed there
  • TanStack Query lives in Client Components for interactive, real-time, or mutation-driven data
  • Hydrate the Query cache from server to avoid client waterfalls on first load
  • Use React Server Components for initial page data; TanStack Query for mutations + polling + optimistic UI

Provider Setup

// providers/query-provider.tsx
'use client'
export function QueryProvider({ children }: { children: React.ReactNode }) {
  const [queryClient] = useState(() => new QueryClient({
    defaultOptions: { queries: { staleTime: 60 * 1000 } },
  }))
  return (
    <QueryClientProvider client={queryClient}>
      {children}
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  )
}

Server Prefetch + HydrationBoundary Pattern

// app/posts/page.tsx  (Server Component)
export default async function PostsPage() {
  const queryClient = new QueryClient()
  await queryClient.prefetchQuery(postsQueryOptions())
  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <PostsList />
    </HydrationBoundary>
  )
}

// app/posts/_components/posts-list.tsx  (Client Component)
'use client'
export function PostsList() {
  const { data: posts } = useQuery(postsQueryOptions())  // reads from pre-populated cache
  return <ul>{posts?.map(p => <li key={p.id}>{p.title}</li>)}</ul>
}

queryOptions Factory

export const postsQueryOptions = (filters?: PostFilters) =>
  queryOptions({
    queryKey: ['posts', 'list', filters],
    queryFn: () => fetch('/api/posts').then(r => r.json()),
  })

Server Actions as mutationFn

// app/posts/actions.ts
'use server'
export async function createPost(data: { title: string; body: string }) {
  const post = await db.post.create({ data })
  revalidatePath('/posts')
  return post
}

// usage in Client Component
const mutation = useMutation({
  mutationFn: createPost,
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ['posts', 'list'] }),
})

Optimistic Updates

const mutation = useMutation({
  mutationFn: updatePost,
  onMutate: async (updated) => {
    await queryClient.cancelQueries({ queryKey: ['posts', 'detail', updated.id] })
    const previous = queryClient.getQueryData(['posts', 'detail', updated.id])
    queryClient.setQueryData(['posts', 'detail', updated.id], (old: Post) => ({ ...old, ...updated }))
    return { previous }
  },
  onError: (_, updated, ctx) => {
    queryClient.setQueryData(['posts', 'detail', updated.id], ctx?.previous)
  },
  onSettled: (_, __, updated) => {
    queryClient.invalidateQueries({ queryKey: ['posts', 'detail', updated.id] })
  },
})

Key Rules

  • Create a new QueryClient per request in Server Components — never reuse across requests
  • Create one QueryClient per browser session via useState in the provider
  • Always wrap server-prefetched subtrees in HydrationBoundary
  • Mark all components using TanStack Query hooks with 'use client'
  • Never call fetch directly in Client Components — always go through queryFn

Related rules

Senior full-stack TypeScript, React, Node.js guidance with clean architecture, testing, and WHY-oriented reasoning.

**/*
41k
via PatrickJS/awesome-cursorrules

Quantitative factor research skills for designing, evaluating, and mining alpha factors in equities markets.

**/*
41k
via PatrickJS/awesome-cursorrules

Android development with Jetpack Compose, clean architecture, and Material Design 3.

**/*
41k
via PatrickJS/awesome-cursorrules

Angular development with Novo Elements UI library using standalone components.

**/*
41k
via PatrickJS/awesome-cursorrules

Expert Angular 18 + TypeScript development with Jest, emphasizing clean code and performance.

**/*
41k
via PatrickJS/awesome-cursorrules

Manage Kubernetes clusters, add-ons, stacks, and credentials via the Ankra CLI platform.

**/*.sh +5
41k
via PatrickJS/awesome-cursorrules