Auto-activate for @tanstack/ imports, useQuery, createRouter. Produces TanStack Router, Query, Table, and Form configurations for React applications. Use when: using useQuery, createRouter, React Query, TanStack Table, file-based routing, data fetching, or SPA state management. Not for non-React frameworks (see vue/svelte/angular) or react-router (TanStack Router is different).
From flownpx claudepluginhub cofin/flow --plugin flowThis skill uses the workspace's default tool permissions.
references/form.mdreferences/query.mdreferences/router.mdreferences/store.mdreferences/table.mdSearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
The TanStack ecosystem provides standard libraries for modern React/TypeScript applications, emphasizing type safety, performance, and developer experience.
import { queryOptions, useQuery } from '@tanstack/react-query'
// Define query options as a factory -- reusable across components and loaders
export const usersQueryOptions = (filters?: UserFilters) =>
queryOptions({
queryKey: ['users', filters],
queryFn: () => api.getUsers(filters),
staleTime: 5 * 60 * 1000, // 5 minutes
})
function UsersPage() {
const { data, isLoading, error } = useQuery(usersQueryOptions())
if (isLoading) return <Spinner />
if (error) return <ErrorMessage error={error} />
return <UserList users={data} />
}
export function useCreateUser() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (data: UserCreate) => api.createUser(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] })
},
})
}
src/routes/
├── __root.tsx # Root layout
├── index.tsx # / route
├── _layout.tsx # Layout wrapper (no URL segment)
├── users/
│ ├── index.tsx # /users
│ ├── $userId.tsx # /users/:userId
│ └── $userId.edit.tsx # /users/:userId/edit
import { createFileRoute } from '@tanstack/react-router'
import { queryClient } from '@/lib/query-client'
export const Route = createFileRoute('/users')({
loader: () => queryClient.ensureQueryData(usersQueryOptions()),
component: UsersPage,
})
// Route parameters
export const Route = createFileRoute('/users/$userId')({
loader: ({ params }) =>
queryClient.ensureQueryData(userQueryOptions(params.userId)),
component: UserDetailPage,
})
import { z } from 'zod'
const searchSchema = z.object({
page: z.number().default(1),
sort: z.enum(['name', 'date']).default('name'),
})
export const Route = createFileRoute('/users')({
validateSearch: searchSchema,
component: UsersPage,
})
import { useReactTable, getCoreRowModel, flexRender, ColumnDef } from '@tanstack/react-table'
const columns: ColumnDef<User>[] = [
{ accessorKey: 'name', header: 'Name' },
{ accessorKey: 'email', header: 'Email' },
{
accessorKey: 'createdAt',
header: 'Joined',
cell: (info) => new Date(info.getValue<string>()).toLocaleDateString(),
},
]
function UsersTable({ users }: { users: User[] }) {
const table = useReactTable({
data: users,
columns,
getCoreRowModel: getCoreRowModel(),
})
// render with flexRender -- see references/table.md
}
<workflow>
| Need | Library | Key Import |
|---|---|---|
| Data fetching & caching | TanStack Query | @tanstack/react-query |
| Client-side routing | TanStack Router | @tanstack/react-router |
| Table / data grid | TanStack Table | @tanstack/react-table |
| Form state & validation | TanStack Form | @tanstack/react-form |
| Lightweight state | TanStack Store | @tanstack/store |
queryOptions() -- always set staleTimecreateFileRoute -- pre-fetch with loaderColumnDef[] typed to your data -- use getCoreRowModel() as baseuseForm() with Zod adapter for validation@/lib/queries/)ensureQueryData in route loaders for data pre-fetchinguseQuery for cache hitsqueryClient.prefetchQuery() for navigation linksRun through the validation checkpoint below before considering the work complete.
</workflow> <guardrails>staleTime on queries -- the default (0) causes unnecessary refetches on every mountqueryKey arrays -- include all variables the query depends on: ['users', filters]queryOptions() factory -- makes query keys reusable across components and loadersisLoading, error from useQuery must be checkedqueryClient.prefetchQuery() in onMouseEnterqueryClient.setQueryData() for optimistic updates<Link> components or hooks between themBefore delivering TanStack code, verify:
useQuery calls have staleTime set (via queryOptions factory or directly)ensureQueryData (not fetchQuery) to leverage cacheColumnDef<T>[]Task: "Create a users list page with TanStack Router + Query, including search, pagination, and prefetch on hover."
// --- lib/queries/users.ts ---
import { queryOptions } from '@tanstack/react-query'
import { api } from '@/lib/api'
interface UserFilters {
search?: string
page?: number
}
export const usersQueryOptions = (filters: UserFilters = {}) =>
queryOptions({
queryKey: ['users', filters],
queryFn: () => api.getUsers(filters),
staleTime: 5 * 60 * 1000,
})
export const userQueryOptions = (userId: string) =>
queryOptions({
queryKey: ['users', userId],
queryFn: () => api.getUser(userId),
staleTime: 5 * 60 * 1000,
})
// --- routes/users/index.tsx ---
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'
import { usersQueryOptions } from '@/lib/queries/users'
import { queryClient } from '@/lib/query-client'
const searchSchema = z.object({
search: z.string().optional(),
page: z.number().default(1),
})
export const Route = createFileRoute('/users/')({
validateSearch: searchSchema,
loader: ({ search }) =>
queryClient.ensureQueryData(usersQueryOptions(search)),
component: UsersPage,
})
function UsersPage() {
const { search, page } = Route.useSearch()
const navigate = Route.useNavigate()
const { data, isLoading, error } = useQuery(
usersQueryOptions({ search, page }),
)
if (isLoading) return <Spinner />
if (error) return <ErrorMessage error={error} />
return (
<div>
<SearchInput
value={search ?? ''}
onChange={(value) => navigate({ search: { search: value, page: 1 } })}
/>
<UserList users={data.items} />
<Pagination
page={page}
totalPages={data.totalPages}
onPageChange={(p) => navigate({ search: { search, page: p } })}
/>
</div>
)
}
// --- components/UserLink.tsx ---
import { Link } from '@tanstack/react-router'
import { useQueryClient } from '@tanstack/react-query'
import { userQueryOptions } from '@/lib/queries/users'
function UserLink({ userId, name }: { userId: string; name: string }) {
const queryClient = useQueryClient()
return (
<Link
to="/users/$userId"
params={{ userId }}
onMouseEnter={() => {
queryClient.prefetchQuery(userQueryOptions(userId))
}}
>
{name}
</Link>
)
}
</example>
For detailed guides and code examples, refer to the following documents in references/: