tanstack

Installation
SKILL.md

TanStack Developer Guide

This skill provides comprehensive patterns and best practices for the TanStack ecosystem in React applications:

  • TanStack Query/DB: Data fetching, caching, collections, live queries, and optimistic updates
  • TanStack Form: Form state management, validation, and field components
  • TanStack Router: File-based routing, type-safe navigation, and URL parameters

Quick Start

For detailed examples and patterns, refer to the following files in the references/ directory:

  • references/query-patterns.md - TanStack Query and TanStack DB patterns
  • references/form-patterns.md - TanStack Form patterns and components
  • references/router-patterns.md - TanStack Router patterns and navigation

TanStack Query/DB Overview

TanStack DB extends TanStack Query with collections, live queries, and optimistic mutations. Key principle: load data into typed collections and consume through live queries that auto-update on data changes.

Critical Rules

  1. Never Use React Query Patterns with Collections - Collections have built-in mutation handling. Do NOT use useMutation + invalidateQueries.

  2. Always Share Collection Instances - Creating new collection instances for mutations causes "key not found" errors. The data-fetching hook must expose the collection, and mutation hooks must receive it as a parameter.

  3. Configure Persistence Handlers - Put server writes in collection handlers (onInsert, onUpdate, onDelete), not mutation hooks.

  4. Single Canonical Collection Pattern - Create ONE collection per entity type. Use live queries for filtered views.

  5. Check Field Changes Properly - Verify fields actually changed in onUpdate, not just that they exist.

Basic Collection Setup

import { createCollection } from '@tanstack/react-db';
import { queryCollectionOptions } from '@tanstack/query-db-collection';
import { z } from 'zod';

const itemSchema = z.object({
  id: z.string(),
  name: z.string().min(1),
  status: z.enum(['active', 'archived']),
});

const itemCollection = createCollection(
  queryCollectionOptions({
    queryKey: ['items'],
    queryFn: async () => (await fetch('/api/items')).json(),
    queryClient,
    getKey: (item) => item.id,
    schema: itemSchema,
  })
);

Sharing Collection Instance (Critical Pattern)

// CORRECT - share the instance
export function useItems() {
  const collection = useMemo(() => createItemsCollection(), []);
  const { data } = useLiveQuery(collection);
  return { data, collection }; // Expose collection
}

export function useUpdateItem(collection: ItemsCollection) {
  return (id, data) => collection.update(id, data);
}

TanStack Form Overview

TanStack Form provides headless form logic with automatic type inference and flexible validation.

Core Principles

  • Type Safety: Types are inferred from default values - avoid manual generic declarations.
  • Headless Design: Build UI components to match your design system.
  • Schema-First Validation: Use Zod for cleaner, more maintainable validation.

Basic Form Setup with createFormHook

import { createFormHookContexts, createFormHook } from '@tanstack/react-form'

export const { fieldContext, formContext, useFieldContext } =
  createFormHookContexts()

export const { useAppForm } = createFormHook({
  fieldContext,
  formContext,
  fieldComponents: {
    TextField,
    SelectField,
  },
  formComponents: {
    SubmitButton,
  },
})

Form Initialization

const form = useAppForm({
  defaultValues: {
    username: '',
    email: '',
    age: 0,
  },
  validators: {
    onChange: schema,
  },
  onSubmit: async ({ value }) => {
    // Handle submission
  },
})

Async Validation with Debouncing

<form.Field
  name="username"
  asyncDebounceMs={500}
  validators={{
    onChangeAsync: async ({ value }) => {
      const isAvailable = await checkUsernameAvailability(value)
      return isAvailable ? undefined : 'Username already taken'
    },
  }}
/>

TanStack Router Overview

TanStack Router provides type-safe file-based routing with first-class TypeScript support.

Core Principles

  • Type-Safe Routing: Embrace type-safe routing as the primary benefit.
  • File-Based Routes: Use file-based routing for scalability.
  • Generated Route Tree: Leverage the generated route tree for type safety.

File Structure

src/routes/
├── __root.tsx          # Root layout with providers
├── _authenticated.tsx  # Auth layout wrapper
├── index.tsx          # Home page (/)
├── posts/
│   ├── index.tsx      # /posts
│   └── $postId.tsx    # /posts/:postId (typed params)
└── settings/
    ├── _layout.tsx    # Settings layout
    └── profile.tsx    # /settings/profile

Basic Route with Search Params

import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'

const searchSchema = z.object({
  page: z.number().min(1).catch(1),
  search: z.string().optional(),
})

export const Route = createFileRoute('/posts/')({
  validateSearch: searchSchema,
  component: PostsList,
})

function PostsList() {
  const { page, search } = Route.useSearch()
  // Use search params...
}

Authentication Layout

// routes/_authenticated.tsx
import { createFileRoute, redirect, Outlet } from '@tanstack/react-router'

export const Route = createFileRoute('/_authenticated')({
  beforeLoad: async ({ location }) => {
    const isAuthenticated = checkAuth()
    if (!isAuthenticated) {
      throw redirect({
        to: '/login',
        search: { redirect: location.href },
      })
    }
  },
  component: () => <Outlet />,
})

Type-Safe Navigation

import { Link, useNavigate } from '@tanstack/react-router'

function Navigation() {
  const navigate = useNavigate()

  return (
    <>
      <Link
        to="/posts/$postId"
        params={{ postId: '123' }}
        search={{ tab: 'comments' }}
      >
        View Post
      </Link>

      <button onClick={() => navigate({ to: '/posts', search: { page: 1 } })}>
        Go to Posts
      </button>
    </>
  )
}

Validation Checklist

Before finishing a task involving TanStack:

Query/DB

  • Collection instances are shared between data-fetching and mutation hooks
  • Persistence handlers (onInsert, onUpdate, onDelete) are configured
  • No useMutation + invalidateQueries patterns with collections
  • One canonical collection per entity type
  • Field changes properly verified in onUpdate handlers

Form

  • Use createFormHook with useAppForm instead of raw useForm for consistency
  • Provide complete default values for proper type inference
  • Use Zod schemas for validation when possible
  • Debounce async validations (minimum 500ms recommended)
  • Prevent default on form submission
  • Display errors with proper accessibility (role="alert")

Router

  • Route path in createFileRoute matches file location
  • Search params use Zod validation with proper defaults (.catch())
  • Loader dependencies are correctly specified in loaderDeps
  • Authentication routes use beforeLoad with proper redirects
  • Navigation uses typed Link or useNavigate hooks
  • Error boundaries are implemented at route level

General

  • Run pnpm run typecheck and pnpm run test

For complete examples, edge cases, and advanced patterns, see the reference files in this skill directory.

Weekly Installs
55
GitHub Stars
301
First Seen
Mar 18, 2026
Installed on
opencode55
gemini-cli55
github-copilot55
codex55
amp55
cline55