{"id":"ebb0d4ad-1d71-44b2-97d8-580a31795a53","shortId":"Svt5BZ","kind":"skill","title":"tanstack","tagline":"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. N","description":"# TanStack Ecosystem\n\nThe TanStack ecosystem provides standard libraries for modern React/TypeScript applications, emphasizing type safety, performance, and developer experience.\n\n## Quick Reference\n\n### useQuery Pattern (with Query Options Factory)\n\n```tsx\nimport { queryOptions, useQuery } from '@tanstack/react-query'\n\n// Define query options as a factory -- reusable across components and loaders\nexport const usersQueryOptions = (filters?: UserFilters) =>\n  queryOptions({\n    queryKey: ['users', filters],\n    queryFn: () => api.getUsers(filters),\n    staleTime: 5 * 60 * 1000, // 5 minutes\n  })\n\nfunction UsersPage() {\n  const { data, isLoading, error } = useQuery(usersQueryOptions())\n\n  if (isLoading) return <Spinner />\n  if (error) return <ErrorMessage error={error} />\n\n  return <UserList users={data} />\n}\n```\n\n### Mutations with Cache Invalidation\n\n```tsx\nexport function useCreateUser() {\n  const queryClient = useQueryClient()\n\n  return useMutation({\n    mutationFn: (data: UserCreate) => api.createUser(data),\n    onSuccess: () => {\n      queryClient.invalidateQueries({ queryKey: ['users'] })\n    },\n  })\n}\n```\n\n### File-Based Routing (TanStack Router)\n\n```text\nsrc/routes/\n├── __root.tsx           # Root layout\n├── index.tsx            # / route\n├── _layout.tsx          # Layout wrapper (no URL segment)\n├── users/\n│   ├── index.tsx        # /users\n│   ├── $userId.tsx      # /users/:userId\n│   └── $userId.edit.tsx # /users/:userId/edit\n```\n\n### Route with Loader & Query Pre-fetching\n\n```tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { queryClient } from '@/lib/query-client'\n\nexport const Route = createFileRoute('/users')({\n  loader: () => queryClient.ensureQueryData(usersQueryOptions()),\n  component: UsersPage,\n})\n\n// Route parameters\nexport const Route = createFileRoute('/users/$userId')({\n  loader: ({ params }) =>\n    queryClient.ensureQueryData(userQueryOptions(params.userId)),\n  component: UserDetailPage,\n})\n```\n\n### Search Parameters (Zod Validation)\n\n```tsx\nimport { z } from 'zod'\n\nconst searchSchema = z.object({\n  page: z.number().default(1),\n  sort: z.enum(['name', 'date']).default('name'),\n})\n\nexport const Route = createFileRoute('/users')({\n  validateSearch: searchSchema,\n  component: UsersPage,\n})\n```\n\n### TanStack Table Basics\n\n```tsx\nimport { useReactTable, getCoreRowModel, flexRender, ColumnDef } from '@tanstack/react-table'\n\nconst columns: ColumnDef<User>[] = [\n  { accessorKey: 'name', header: 'Name' },\n  { accessorKey: 'email', header: 'Email' },\n  {\n    accessorKey: 'createdAt',\n    header: 'Joined',\n    cell: (info) => new Date(info.getValue<string>()).toLocaleDateString(),\n  },\n]\n\nfunction UsersTable({ users }: { users: User[] }) {\n  const table = useReactTable({\n    data: users,\n    columns,\n    getCoreRowModel: getCoreRowModel(),\n  })\n  // render with flexRender -- see references/table.md\n}\n```\n\n<workflow>\n\n## Workflow\n\n### Step 1: Identify the Library\n\n| Need | Library | Key Import |\n| --- | --- | --- |\n| Data fetching & caching | TanStack Query | `@tanstack/react-query` |\n| Client-side routing | TanStack Router | `@tanstack/react-router` |\n| Table / data grid | TanStack Table | `@tanstack/react-table` |\n| Form state & validation | TanStack Form | `@tanstack/react-form` |\n| Lightweight state | TanStack Store | `@tanstack/store` |\n\n### Step 2: Implement\n\n1. **Query**: Define query options factories with `queryOptions()` -- always set `staleTime`\n2. **Router**: Use file-based routing with `createFileRoute` -- pre-fetch with `loader`\n3. **Table**: Define `ColumnDef[]` typed to your data -- use `getCoreRowModel()` as base\n4. **Form**: Use `useForm()` with Zod adapter for validation\n\n### Step 3: Integrate Router + Query\n\n1. Create query options factories in a shared location (e.g., `@/lib/queries/`)\n2. Use `ensureQueryData` in route loaders for data pre-fetching\n3. Use the same query options in components with `useQuery` for cache hits\n4. Prefetch on hover with `queryClient.prefetchQuery()` for navigation links\n\n### Step 4: Validate\n\nRun through the validation checkpoint below before considering the work complete.\n\n</workflow>\n\n<guardrails>\n\n## Guardrails\n\n- **Always set `staleTime`** on queries -- the default (0) causes unnecessary refetches on every mount\n- **Always use `queryKey` arrays** -- include all variables the query depends on: `['users', filters]`\n- **Always use `queryOptions()` factory** -- makes query keys reusable across components and loaders\n- **Always handle loading and error states** -- `isLoading`, `error` from `useQuery` must be checked\n- **Prefetch on hover** for navigation links -- use `queryClient.prefetchQuery()` in `onMouseEnter`\n- **Never use inline queryFn without queryKey** -- keys must be stable and serializable\n- **Never mutate query data directly** -- use `queryClient.setQueryData()` for optimistic updates\n- **TanStack Router is NOT react-router** -- do not mix `<Link>` components or hooks between them\n\n</guardrails>\n\n<validation>\n\n### Validation Checkpoint\n\nBefore delivering TanStack code, verify:\n\n- [ ] All `useQuery` calls have `staleTime` set (via `queryOptions` factory or directly)\n- [ ] Query keys include all dependent variables (no stale closures)\n- [ ] Loading and error states are handled in every component that fetches data\n- [ ] Route loaders use `ensureQueryData` (not `fetchQuery`) to leverage cache\n- [ ] Mutations invalidate related query keys on success\n- [ ] Table column definitions are typed with `ColumnDef<T>[]`\n\n</validation>\n\n<example>\n\n## Example\n\n**Task:** \"Create a users list page with TanStack Router + Query, including search, pagination, and prefetch on hover.\"\n\n```tsx\n// --- lib/queries/users.ts ---\nimport { queryOptions } from '@tanstack/react-query'\nimport { api } from '@/lib/api'\n\ninterface UserFilters {\n  search?: string\n  page?: number\n}\n\nexport const usersQueryOptions = (filters: UserFilters = {}) =>\n  queryOptions({\n    queryKey: ['users', filters],\n    queryFn: () => api.getUsers(filters),\n    staleTime: 5 * 60 * 1000,\n  })\n\nexport const userQueryOptions = (userId: string) =>\n  queryOptions({\n    queryKey: ['users', userId],\n    queryFn: () => api.getUser(userId),\n    staleTime: 5 * 60 * 1000,\n  })\n\n\n// --- routes/users/index.tsx ---\nimport { createFileRoute } from '@tanstack/react-router'\nimport { z } from 'zod'\nimport { usersQueryOptions } from '@/lib/queries/users'\nimport { queryClient } from '@/lib/query-client'\n\nconst searchSchema = z.object({\n  search: z.string().optional(),\n  page: z.number().default(1),\n})\n\nexport const Route = createFileRoute('/users/')({\n  validateSearch: searchSchema,\n  loader: ({ search }) =>\n    queryClient.ensureQueryData(usersQueryOptions(search)),\n  component: UsersPage,\n})\n\nfunction UsersPage() {\n  const { search, page } = Route.useSearch()\n  const navigate = Route.useNavigate()\n  const { data, isLoading, error } = useQuery(\n    usersQueryOptions({ search, page }),\n  )\n\n  if (isLoading) return <Spinner />\n  if (error) return <ErrorMessage error={error} />\n\n  return (\n    <div>\n      <SearchInput\n        value={search ?? ''}\n        onChange={(value) => navigate({ search: { search: value, page: 1 } })}\n      />\n      <UserList users={data.items} />\n      <Pagination\n        page={page}\n        totalPages={data.totalPages}\n        onPageChange={(p) => navigate({ search: { search, page: p } })}\n      />\n    </div>\n  )\n}\n\n\n// --- components/UserLink.tsx ---\nimport { Link } from '@tanstack/react-router'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { userQueryOptions } from '@/lib/queries/users'\n\nfunction UserLink({ userId, name }: { userId: string; name: string }) {\n  const queryClient = useQueryClient()\n\n  return (\n    <Link\n      to=\"/users/$userId\"\n      params={{ userId }}\n      onMouseEnter={() => {\n        queryClient.prefetchQuery(userQueryOptions(userId))\n      }}\n    >\n      {name}\n    </Link>\n  )\n}\n```\n\n</example>\n\n## References Index\n\nFor detailed guides and code examples, refer to the following documents in `references/`:\n\n- **[Router](references/router.md)** -- File-based routing, parameters, navigation, and loaders.\n- **[Query](references/query.md)** -- Cache management, query factories, mutations, and optimistic updates.\n- **[Table](references/table.md)** -- Headless table logic and integration.\n- **[Form](references/form.md)** -- State management and validation adapters (Zod).\n- **[Store](references/store.md)** -- Lightweight client-side state management.\n\n## Official References\n\n- <https://tanstack.com/router/>\n- <https://tanstack.com/query/>\n- <https://tanstack.com/table/>\n- <https://tanstack.com/form/>\n- <https://tanstack.com/store/>\n\n## Shared Styleguide Baseline\n\n- Use shared styleguides for generic language/framework rules to reduce duplication in this skill.\n- [General Principles](https://github.com/cofin/flow/blob/main/templates/styleguides/general.md)\n- [TanStack](https://github.com/cofin/flow/blob/main/templates/styleguides/frameworks/tanstack.md)\n- [TypeScript](https://github.com/cofin/flow/blob/main/templates/styleguides/languages/typescript.md)\n- Keep this skill focused on tool-specific workflows, edge cases, and integration details.","tags":["tanstack","flow","cofin","agent-skills","ai-agents","beads","claude-code","codex","cursor","developer-tools","gemini-cli","opencode"],"capabilities":["skill","source-cofin","skill-tanstack","topic-agent-skills","topic-ai-agents","topic-beads","topic-claude-code","topic-codex","topic-cursor","topic-developer-tools","topic-gemini-cli","topic-opencode","topic-plugin","topic-slash-commands","topic-spec-driven-development"],"categories":["flow"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/cofin/flow/tanstack","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add cofin/flow","source_repo":"https://github.com/cofin/flow","install_from":"skills.sh"}},"qualityScore":"0.455","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 11 github stars · SKILL.md body (9,180 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-24T07:03:20.550Z","embedding":null,"createdAt":"2026-04-23T13:04:02.207Z","updatedAt":"2026-04-24T07:03:20.550Z","lastSeenAt":"2026-04-24T07:03:20.550Z","tsv":"'/cofin/flow/blob/main/templates/styleguides/frameworks/tanstack.md)':905 '/cofin/flow/blob/main/templates/styleguides/general.md)':901 '/cofin/flow/blob/main/templates/styleguides/languages/typescript.md)':909 '/form/':877 '/lib/api':637 '/lib/queries':400 '/lib/queries/users':688,782 '/lib/query-client':189,692 '/query/':871 '/router/':868 '/store/':880 '/table/':874 '/users':167,169,172,194,206,241,707,797 '0':456 '1':230,298,339,390,702,754 '1000':100,659,675 '2':337,350,401 '3':364,386,412 '4':376,425,435 '5':98,101,657,673 '60':99,658,674 'accessorkey':260,264,268 'across':81,484 'activ':4 'adapt':382,854 'alway':347,449,463,476,488 'api':635 'api.createuser':140 'api.getuser':670 'api.getusers':95,654 'applic':20,52 'array':466 'auto':3 'auto-activ':2 'base':32,148,355,375,825 'baselin':883 'basic':248 'cach':126,308,423,595,833 'call':557 'case':920 'caus':457 'cell':272 'check':500 'checkpoint':441,549 'client':313,860 'client-sid':312,859 'closur':574 'code':553,812 'column':258,288,604 'columndef':254,259,367,609 'complet':447 'compon':82,198,213,244,419,485,543,583,715 'components/userlink.tsx':770 'configur':17 'consid':444 'const':86,105,132,191,203,224,238,257,283,645,661,693,704,719,723,726,791 'creat':391,612 'createdat':269 'createfilerout':183,193,205,240,358,678,706 'createrout':9,25 'data':34,106,123,138,141,286,306,320,371,408,526,586,727 'data.items':757 'data.totalpages':762 'date':234,275 'default':229,235,455,701 'defin':74,341,366 'definit':605 'deliv':551 'depend':472,570 'detail':809,923 'develop':58 'direct':527,565 'document':818 'duplic':893 'e.g':399 'ecosystem':42,45 'edg':919 'email':265,267 'emphas':53 'ensurequerydata':403,590 'error':108,115,118,119,492,495,577,729,738,741,742 'errormessag':117,740 'everi':461,582 'exampl':610,813 'experi':59 'export':85,129,190,202,237,644,660,703 'factori':67,79,344,394,479,563,836 'fetch':35,180,307,361,411,585 'fetchqueri':592 'file':31,147,354,824 'file-bas':30,146,353,823 'filter':88,93,96,475,647,652,655 'flexrend':253,293 'focus':913 'follow':817 'form':16,325,329,377,848 'function':103,130,278,717,783 'general':897 'generic':888 'getcorerowmodel':252,289,290,373 'github.com':900,904,908 'github.com/cofin/flow/blob/main/templates/styleguides/frameworks/tanstack.md)':903 'github.com/cofin/flow/blob/main/templates/styleguides/general.md)':899 'github.com/cofin/flow/blob/main/templates/styleguides/languages/typescript.md)':907 'grid':321 'guardrail':448 'guid':810 'handl':489,580 'header':262,266,270 'headless':843 'hit':424 'hook':545 'hover':428,503,627 'identifi':299 'implement':338 'import':7,69,182,186,220,250,305,630,634,677,681,685,689,771,775,779 'includ':467,568,621 'index':807 'index.tsx':157,166 'info':273 'info.getvalue':276 'inlin':513 'integr':387,847,922 'interfac':638 'invalid':127,597 'isload':107,112,494,728,735 'join':271 'keep':910 'key':304,482,517,567,600 'language/framework':889 'layout':156,160 'layout.tsx':159 'leverag':594 'lib/queries/users.ts':629 'librari':48,301,303 'lightweight':331,858 'link':433,506,772,795 'list':615 'load':490,575 'loader':84,176,195,208,363,406,487,588,710,830 'locat':398 'logic':845 'make':480 'manag':39,834,851,863 'minut':102 'mix':542 'modern':50 'mount':462 'must':498,518 'mutat':124,524,596,837 'mutationfn':137 'n':40 'name':233,236,261,263,786,789,805 'navig':432,505,724,749,765,828 'need':302 'never':511,523 'new':274 'number':643 'offici':864 'onchang':747 'onmouseent':510,801 'onpagechang':763 'onsuccess':142 'optimist':531,839 'option':66,76,343,393,417,698 'p':764,769 'page':227,616,642,699,721,733,753,759,760,768 'pagin':623,758 'param':209,799 'paramet':201,216,827 'params.userid':212 'pattern':63 'perform':56 'pre':179,360,410 'pre-fetch':178,359,409 'prefetch':426,501,625 'principl':898 'produc':10 'provid':46 'queri':13,27,65,75,177,310,340,342,389,392,416,453,471,481,525,566,599,620,831,835 'querycli':133,187,690,792 'queryclient.ensurequerydata':196,210,712 'queryclient.invalidatequeries':143 'queryclient.prefetchquery':430,508,802 'queryclient.setquerydata':529 'queryfn':94,514,653,669 'querykey':91,144,465,516,650,666 'queryopt':70,90,346,478,562,631,649,665 'quick':60 'react':19,26,538 'react-rout':537 'react/typescript':51 'reduc':892 'refer':61,806,814,820,865 'references/form.md':849 'references/query.md':832 'references/router.md':822 'references/store.md':857 'references/table.md':295,842 'refetch':459 'relat':598 'render':291 'return':113,116,120,135,736,739,743,794 'reusabl':80,483 'root':155 'root.tsx':154 'rout':33,149,158,174,192,200,204,239,315,356,405,587,705,826 'route.usenavigate':725 'route.usesearch':722 'router':12,151,317,351,388,534,539,619,821 'routes/users/index.tsx':676 'rule':890 'run':437 'safeti':55 'search':215,622,640,696,711,714,720,732,746,750,751,766,767 'searchinput':744 'searchschema':225,243,694,709 'see':294 'segment':164 'serializ':522 'set':348,450,560 'share':397,881,885 'side':314,861 'skill':896,912 'skill-tanstack' 'sort':231 'source-cofin' 'spa':37 'specif':917 'src/routes':153 'stabl':520 'stale':573 'staletim':97,349,451,559,656,672 'standard':47 'state':38,326,332,493,578,850,862 'step':297,336,385,434 'store':334,856 'string':641,664,788,790 'styleguid':882,886 'success':602 'tabl':14,29,247,284,319,323,365,603,841,844 'tanstack':1,6,11,28,41,44,150,246,309,316,322,328,333,533,552,618,902 'tanstack.com':867,870,873,876,879 'tanstack.com/form/':875 'tanstack.com/query/':869 'tanstack.com/router/':866 'tanstack.com/store/':878 'tanstack.com/table/':872 'tanstack/react-form':330 'tanstack/react-query':73,311,633,778 'tanstack/react-router':185,318,680,774 'tanstack/react-table':256,324 'tanstack/store':335 'task':611 'text':152 'tolocaledatestr':277 'tool':916 'tool-specif':915 'topic-agent-skills' 'topic-ai-agents' 'topic-beads' 'topic-claude-code' 'topic-codex' 'topic-cursor' 'topic-developer-tools' 'topic-gemini-cli' 'topic-opencode' 'topic-plugin' 'topic-slash-commands' 'topic-spec-driven-development' 'totalpag':761 'tsx':68,128,181,219,249,628 'type':54,368,607 'typescript':906 'unnecessari':458 'updat':532,840 'url':163 'use':21,23,352,372,378,402,413,464,477,507,512,528,589,884 'usecreateus':131 'useform':379 'usemut':136 'usequeri':8,24,62,71,109,421,497,556,730 'usequerycli':134,776,793 'user':92,122,145,165,280,281,282,287,474,614,651,667,756 'usercr':139 'userdetailpag':214 'usereactt':251,285 'userfilt':89,639,648 'userid':170,207,663,668,671,785,787,798,800,804 'userid.edit.tsx':171 'userid.tsx':168 'userid/edit':173 'userlink':784 'userlist':121,755 'userqueryopt':211,662,780,803 'userspag':104,199,245,716,718 'usersqueryopt':87,110,197,646,686,713,731 'userst':279 'valid':218,327,384,436,440,548,853 'validatesearch':242,708 'valu':745,748,752 'variabl':469,571 'verifi':554 'via':561 'without':515 'work':446 'workflow':296,918 'wrapper':161 'z':221,682 'z.enum':232 'z.number':228,700 'z.object':226,695 'z.string':697 'zod':217,223,381,684,855","prices":[{"id":"b2e48e6e-eed5-4ec5-8f11-1318ad5a4aa0","listingId":"ebb0d4ad-1d71-44b2-97d8-580a31795a53","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"cofin","category":"flow","install_from":"skills.sh"},"createdAt":"2026-04-23T13:04:02.207Z"}],"sources":[{"listingId":"ebb0d4ad-1d71-44b2-97d8-580a31795a53","source":"github","sourceId":"cofin/flow/tanstack","sourceUrl":"https://github.com/cofin/flow/tree/main/skills/tanstack","isPrimary":false,"firstSeenAt":"2026-04-23T13:04:02.207Z","lastSeenAt":"2026-04-24T07:03:20.550Z"}],"details":{"listingId":"ebb0d4ad-1d71-44b2-97d8-580a31795a53","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cofin","slug":"tanstack","github":{"repo":"cofin/flow","stars":11,"topics":["agent-skills","ai-agents","beads","claude-code","codex","context-driven-development","cursor","developer-tools","gemini-cli","opencode","plugin","slash-commands","spec-driven-development","subagents","tdd","workflow"],"license":"apache-2.0","html_url":"https://github.com/cofin/flow","pushed_at":"2026-04-19T23:22:27Z","description":"Context-Driven Development toolkit for AI agents — spec-first planning, TDD workflow, and Beads integration.","skill_md_sha":"2b78022286801b8c827a20f3073bcc45c6107f3e","skill_md_path":"skills/tanstack/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cofin/flow/tree/main/skills/tanstack"},"layout":"multi","source":"github","category":"flow","frontmatter":{"name":"tanstack","description":"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)."},"skills_sh_url":"https://skills.sh/cofin/flow/tanstack"},"updatedAt":"2026-04-24T07:03:20.550Z"}}