tanstack-db — tanstack-db install tanstack-db, ispo-code, community, tanstack-db install, ide skills, tanstack-db reactive client store, tanstack-db local-first apps, tanstack-db real-time sync, Claude Code, Cursor, Windsurf

v1.0.0
GitHub

About this Skill

Perfect for Real-Time Application Agents needing sub-millisecond queries and optimistic mutations with TanStack DB tanstack-db is a reactive client store for building local-first apps with features like typed collections and live queries.

Features

Typed set of objects via Collections
Reactive query updates with Live Query
Instant local writes with Optimistic Mutation
Real-time sync for collaborative applications
Sub-millisecond queries for fast data retrieval
Incremental updates for efficient data management

# Core Topics

olegakbarov olegakbarov
[0]
[0]
Updated: 3/8/2026

Agent Capability Analysis

The tanstack-db skill by olegakbarov is an open-source community AI agent skill for Claude Code and other IDE workflows, helping agents execute tasks with better context, repeatability, and domain-specific guidance. Optimized for tanstack-db install, tanstack-db reactive client store, tanstack-db local-first apps.

Ideal Agent Persona

Perfect for Real-Time Application Agents needing sub-millisecond queries and optimistic mutations with TanStack DB

Core Value

Empowers agents to build local-first apps with reactive client stores, leveraging live queries, optimistic mutations, and real-time sync using TanStack DB's comprehensive features, including collections and live queries

Capabilities Granted for tanstack-db

Building real-time data visualizations with sub-millisecond queries
Implementing optimistic mutations for instant local writes
Syncing data in real-time across applications with TanStack DB

! Prerequisites & Limits

  • Currently in BETA
  • Requires understanding of reactive client stores and local-first app development
Labs Demo

Browser Sandbox Environment

⚡️ Ready to unleash?

Experience this Agent in a zero-setup browser environment powered by WebContainers. No installation required.

Boot Container Sandbox

tanstack-db

Install tanstack-db, an AI agent skill for AI agent workflows and automation. Works with Claude Code, Cursor, and Windsurf with one-command setup.

SKILL.md
Readonly

TanStack DB Skill

Expert guidance for TanStack DB - the reactive client store for building local-first apps with sub-millisecond queries, optimistic mutations, and real-time sync.

Note: TanStack DB is currently in BETA.

Quick Reference

Core Concepts

ConceptPurpose
CollectionTyped set of objects (like a DB table)
Live QueryReactive query that updates incrementally
Optimistic MutationInstant local write, synced in background
Sync EngineReal-time data sync (Electric, RxDB, PowerSync)

Project Structure

src/
├── collections/
│   ├── todos.ts         # Todo collection definition
│   ├── users.ts         # User collection
│   └── index.ts         # Export all collections
├── queries/
│   └── hooks.ts         # Custom live query hooks
└── lib/
    └── db.ts            # DB setup & QueryClient

Installation

bash
1# Core + React 2npm install @tanstack/react-db @tanstack/db 3 4# With TanStack Query (REST APIs) 5npm install @tanstack/query-db-collection @tanstack/react-query 6 7# With ElectricSQL (Postgres sync) 8npm install @tanstack/electric-db-collection 9 10# With RxDB (offline-first) 11npm install @tanstack/rxdb-db-collection rxdb

Collections

Query Collection (REST API)

typescript
1// src/collections/todos.ts 2import { createCollection } from "@tanstack/react-db" 3import { queryCollectionOptions } from "@tanstack/query-db-collection" 4import { queryClient } from "@/lib/db" 5 6export interface Todo { 7 id: string 8 text: string 9 completed: boolean 10 createdAt: string 11} 12 13export const todosCollection = createCollection( 14 queryCollectionOptions({ 15 queryKey: ["todos"], 16 queryFn: async () => { 17 const res = await fetch("/api/todos") 18 return res.json() as Promise<Todo[]> 19 }, 20 queryClient, 21 getKey: (item) => item.id, 22 23 // Persistence handlers 24 onInsert: async ({ transaction }) => { 25 const items = transaction.mutations.map((m) => m.modified) 26 await fetch("/api/todos", { 27 method: "POST", 28 body: JSON.stringify(items), 29 }) 30 }, 31 32 onUpdate: async ({ transaction }) => { 33 await Promise.all( 34 transaction.mutations.map((m) => 35 fetch(`/api/todos/${m.key}`, { 36 method: "PATCH", 37 body: JSON.stringify(m.changes), 38 }) 39 ) 40 ) 41 }, 42 43 onDelete: async ({ transaction }) => { 44 await Promise.all( 45 transaction.mutations.map((m) => 46 fetch(`/api/todos/${m.key}`, { method: "DELETE" }) 47 ) 48 ) 49 }, 50 }) 51)

Electric Collection (Postgres Sync)

typescript
1// src/collections/todos.ts 2import { createCollection } from "@tanstack/react-db" 3import { electricCollectionOptions } from "@tanstack/electric-db-collection" 4 5export const todosCollection = createCollection( 6 electricCollectionOptions({ 7 id: "todos", 8 shapeOptions: { 9 url: "/api/electric/todos", // Proxy to Electric 10 }, 11 getKey: (item) => item.id, 12 13 // Use transaction ID for sync confirmation 14 onInsert: async ({ transaction }) => { 15 const item = transaction.mutations[0].modified 16 const res = await fetch("/api/todos", { 17 method: "POST", 18 body: JSON.stringify(item), 19 }) 20 const { txid } = await res.json() 21 return { txid } // Electric waits for this txid 22 }, 23 24 onUpdate: async ({ transaction }) => { 25 const { key, changes } = transaction.mutations[0] 26 const res = await fetch(`/api/todos/${key}`, { 27 method: "PATCH", 28 body: JSON.stringify(changes), 29 }) 30 const { txid } = await res.json() 31 return { txid } 32 }, 33 34 onDelete: async ({ transaction }) => { 35 const { key } = transaction.mutations[0] 36 const res = await fetch(`/api/todos/${key}`, { method: "DELETE" }) 37 const { txid } = await res.json() 38 return { txid } 39 }, 40 }) 41)

Sync Modes

typescript
1// Eager (default): Load all upfront - best for <10k rows 2electricCollectionOptions({ 3 sync: { mode: "eager" }, 4 // ... 5}) 6 7// On-demand: Load only what queries request - best for >50k rows 8electricCollectionOptions({ 9 sync: { mode: "on-demand" }, 10 // ... 11}) 12 13// Progressive: Instant query results + background full sync 14electricCollectionOptions({ 15 sync: { mode: "progressive" }, 16 // ... 17})

Live Queries

Basic Query

tsx
1import { useLiveQuery } from "@tanstack/react-db" 2import { todosCollection } from "@/collections/todos" 3 4function TodoList() { 5 const { data: todos, isLoading } = useLiveQuery((q) => 6 q.from({ todo: todosCollection }) 7 ) 8 9 if (isLoading) return <div>Loading...</div> 10 11 return ( 12 <ul> 13 {todos?.map((todo) => ( 14 <li key={todo.id}>{todo.text}</li> 15 ))} 16 </ul> 17 ) 18}

Filtering with Where

typescript
1import { eq, gt, and, or, like, inArray } from "@tanstack/react-db" 2 3// Simple equality 4useLiveQuery((q) => 5 q.from({ todo: todosCollection }) 6 .where(({ todo }) => eq(todo.completed, false)) 7) 8 9// Multiple conditions 10useLiveQuery((q) => 11 q.from({ todo: todosCollection }) 12 .where(({ todo }) => 13 and( 14 eq(todo.completed, false), 15 gt(todo.priority, 5) 16 ) 17 ) 18) 19 20// OR conditions 21useLiveQuery((q) => 22 q.from({ todo: todosCollection }) 23 .where(({ todo }) => 24 or( 25 eq(todo.status, "urgent"), 26 eq(todo.status, "high") 27 ) 28 ) 29) 30 31// String matching 32useLiveQuery((q) => 33 q.from({ todo: todosCollection }) 34 .where(({ todo }) => like(todo.text, "%meeting%")) 35) 36 37// In array 38useLiveQuery((q) => 39 q.from({ todo: todosCollection }) 40 .where(({ todo }) => inArray(todo.id, ["1", "2", "3"])) 41)

Comparison Operators

OperatorDescription
eq(a, b)Equal
gt(a, b)Greater than
gte(a, b)Greater than or equal
lt(a, b)Less than
lte(a, b)Less than or equal
like(a, pattern)Case-sensitive match
ilike(a, pattern)Case-insensitive match
inArray(a, arr)Value in array
isNull(a)Is null
isUndefined(a)Is undefined

Sorting and Pagination

typescript
1useLiveQuery((q) => 2 q.from({ todo: todosCollection }) 3 .orderBy(({ todo }) => todo.createdAt, "desc") 4 .limit(20) 5 .offset(0) 6)

Select Projection

typescript
1// Select specific fields 2useLiveQuery((q) => 3 q.from({ todo: todosCollection }) 4 .select(({ todo }) => ({ 5 id: todo.id, 6 text: todo.text, 7 done: todo.completed, 8 })) 9) 10 11// Computed fields 12useLiveQuery((q) => 13 q.from({ todo: todosCollection }) 14 .select(({ todo }) => ({ 15 id: todo.id, 16 displayText: upper(todo.text), 17 isOverdue: lt(todo.dueDate, new Date().toISOString()), 18 })) 19)

Joins

typescript
1import { usersCollection } from "@/collections/users" 2 3// Inner join 4useLiveQuery((q) => 5 q.from({ todo: todosCollection }) 6 .join( 7 { user: usersCollection }, 8 ({ todo, user }) => eq(todo.userId, user.id), 9 "inner" 10 ) 11 .select(({ todo, user }) => ({ 12 id: todo.id, 13 text: todo.text, 14 assignee: user.name, 15 })) 16) 17 18// Left join (default) 19useLiveQuery((q) => 20 q.from({ todo: todosCollection }) 21 .leftJoin( 22 { user: usersCollection }, 23 ({ todo, user }) => eq(todo.userId, user.id) 24 ) 25)

Aggregations

typescript
1// Group by with aggregates 2useLiveQuery((q) => 3 q.from({ todo: todosCollection }) 4 .groupBy(({ todo }) => todo.status) 5 .select(({ todo }) => ({ 6 status: todo.status, 7 count: count(todo.id), 8 avgPriority: avg(todo.priority), 9 })) 10) 11 12// With having clause 13useLiveQuery((q) => 14 q.from({ order: ordersCollection }) 15 .groupBy(({ order }) => order.customerId) 16 .select(({ order }) => ({ 17 customerId: order.customerId, 18 totalSpent: sum(order.amount), 19 })) 20 .having(({ $selected }) => gt($selected.totalSpent, 1000)) 21)

Find Single Item

typescript
1// Returns T | undefined 2useLiveQuery((q) => 3 q.from({ todo: todosCollection }) 4 .where(({ todo }) => eq(todo.id, todoId)) 5 .findOne() 6)

Reactive Dependencies

typescript
1// Re-run query when deps change 2const [filter, setFilter] = useState("all") 3 4useLiveQuery( 5 (q) => 6 q.from({ todo: todosCollection }) 7 .where(({ todo }) => 8 filter === "all" ? true : eq(todo.status, filter) 9 ), 10 [filter] // Dependency array 11)

Mutations

Basic Operations

typescript
1const { collection } = useLiveQuery((q) => 2 q.from({ todo: todosCollection }) 3) 4 5// Insert 6collection.insert({ 7 id: crypto.randomUUID(), 8 text: "New todo", 9 completed: false, 10 createdAt: new Date().toISOString(), 11}) 12 13// Insert multiple 14collection.insert([item1, item2, item3]) 15 16// Update (immutable draft pattern) 17collection.update(todoId, (draft) => { 18 draft.completed = true 19 draft.completedAt = new Date().toISOString() 20}) 21 22// Update multiple 23collection.update([id1, id2], (drafts) => { 24 drafts.forEach((d) => (d.completed = true)) 25}) 26 27// Delete 28collection.delete(todoId) 29 30// Delete multiple 31collection.delete([id1, id2, id3])

Non-Optimistic Mutations

typescript
1// Skip optimistic update, wait for server 2collection.insert(item, { optimistic: false }) 3collection.update(id, updater, { optimistic: false }) 4collection.delete(id, { optimistic: false })

Custom Optimistic Actions

typescript
1import { createOptimisticAction } from "@tanstack/react-db" 2 3// Multi-collection or complex mutations 4const likePost = createOptimisticAction<string>({ 5 onMutate: (postId) => { 6 postsCollection.update(postId, (draft) => { 7 draft.likeCount += 1 8 draft.likedByMe = true 9 }) 10 }, 11 mutationFn: async (postId) => { 12 await fetch(`/api/posts/${postId}/like`, { method: "POST" }) 13 // Optionally refetch 14 await postsCollection.utils.refetch() 15 }, 16}) 17 18// Usage 19likePost.mutate(postId)

Manual Transactions

typescript
1import { createTransaction } from "@tanstack/react-db" 2 3const tx = createTransaction({ 4 autoCommit: false, 5 mutationFn: async ({ transaction }) => { 6 // Batch all mutations in single request 7 await fetch("/api/batch", { 8 method: "POST", 9 body: JSON.stringify(transaction.mutations), 10 }) 11 }, 12}) 13 14// Queue mutations 15tx.mutate(() => { 16 todosCollection.insert(newTodo) 17 todosCollection.update(existingId, (d) => (d.status = "active")) 18 todosCollection.delete(oldId) 19}) 20 21// Commit or rollback 22await tx.commit() 23// or 24tx.rollback()

Paced Mutations (Debounce/Throttle)

typescript
1import { usePacedMutations, debounceStrategy } from "@tanstack/react-db" 2 3// Debounce rapid updates (e.g., text input) 4const { mutate } = usePacedMutations({ 5 onMutate: (value: string) => { 6 todosCollection.update(todoId, (d) => (d.text = value)) 7 }, 8 mutationFn: async ({ transaction }) => { 9 const changes = transaction.mutations[0].changes 10 await fetch(`/api/todos/${todoId}`, { 11 method: "PATCH", 12 body: JSON.stringify(changes), 13 }) 14 }, 15 strategy: debounceStrategy({ wait: 500 }), 16}) 17 18// Usage in input 19<input onChange={(e) => mutate(e.target.value)} />

Provider Setup

tsx
1// src/main.tsx 2import { QueryClient, QueryClientProvider } from "@tanstack/react-query" 3import { DBProvider } from "@tanstack/react-db" 4 5const queryClient = new QueryClient() 6 7function App() { 8 return ( 9 <QueryClientProvider client={queryClient}> 10 <DBProvider> 11 <Router /> 12 </DBProvider> 13 </QueryClientProvider> 14 ) 15}

Electric Backend Setup

Server-Side Transaction ID

typescript
1// api/todos/route.ts (example with Drizzle) 2import { db } from "@/db" 3import { todos } from "@/db/schema" 4import { sql } from "drizzle-orm" 5 6export async function POST(req: Request) { 7 const data = await req.json() 8 9 const result = await db.transaction(async (tx) => { 10 // Insert the todo 11 const [todo] = await tx.insert(todos).values(data).returning() 12 13 // Get transaction ID in SAME transaction 14 const [{ txid }] = await tx.execute( 15 sql`SELECT pg_current_xact_id()::text as txid` 16 ) 17 18 return { todo, txid: parseInt(txid, 10) } 19 }) 20 21 return Response.json(result) 22}

Electric Proxy Route

typescript
1// api/electric/[...path]/route.ts 2export async function GET(req: Request) { 3 const url = new URL(req.url) 4 const electricUrl = `${process.env.ELECTRIC_URL}${url.pathname}${url.search}` 5 6 return fetch(electricUrl, { 7 headers: { Authorization: `Bearer ${process.env.ELECTRIC_TOKEN}` }, 8 }) 9}

Utility Methods

typescript
1// Refetch collection data 2await collection.utils.refetch() 3 4// Direct writes (bypass optimistic state) 5collection.utils.writeInsert(item) 6collection.utils.writeUpdate(item) 7collection.utils.writeDelete(id) 8collection.utils.writeUpsert(item) 9 10// Batch direct writes 11collection.utils.writeBatch(() => { 12 collection.utils.writeInsert(item1) 13 collection.utils.writeDelete(id2) 14}) 15 16// Wait for Electric sync (with txid) 17await collection.utils.awaitTxId(txid, 30000) 18 19// Wait for custom match 20await collection.utils.awaitMatch( 21 (msg) => msg.value.id === expectedId, 22 5000 23)

Gotchas and Tips

  1. Queries run client-side: TanStack DB is NOT an ORM - queries run locally against collections, not against a database
  2. Sub-millisecond updates: Uses differential dataflow - only recalculates affected parts of queries
  3. Transaction IDs matter: With Electric, always get pg_current_xact_id() in the SAME transaction as mutations
  4. Sync modes: Use "eager" for small datasets, "on-demand" for large, "progressive" for collaborative apps
  5. Optimistic by default: All mutations apply instantly; use { optimistic: false } for server-validated operations
  6. Fine-grained reactivity: Only components using changed data re-render
  7. Mutation merging: Rapid updates merge automatically (insert+update→insert, update+update→merged)
  8. Collection = complete state: Empty array from queryFn clears the collection

Common Patterns

Loading States

tsx
1function TodoList() { 2 const { data, isLoading, isPending } = useLiveQuery((q) => 3 q.from({ todo: todosCollection }) 4 ) 5 6 if (isLoading) return <Skeleton /> 7 if (!data?.length) return <EmptyState /> 8 9 return <List items={data} /> 10}

Mutation with Feedback

tsx
1function TodoItem({ todo }: { todo: Todo }) { 2 const { collection } = useLiveQuery((q) => 3 q.from({ todo: todosCollection }) 4 ) 5 6 const toggle = async () => { 7 const tx = collection.update(todo.id, (d) => { 8 d.completed = !d.completed 9 }) 10 11 try { 12 await tx.isPersisted.promise 13 toast.success("Saved!") 14 } catch (err) { 15 toast.error("Failed to save") 16 // Optimistic update already rolled back 17 } 18 } 19 20 return <Checkbox checked={todo.completed} onChange={toggle} /> 21}

Derived/Computed Collections

tsx
1// Create a "view" with live query 2const completedTodos = useLiveQuery((q) => 3 q.from({ todo: todosCollection }) 4 .where(({ todo }) => eq(todo.completed, true)) 5 .orderBy(({ todo }) => todo.completedAt, "desc") 6)

FAQ & Installation Steps

These questions and steps mirror the structured data on this page for better search understanding.

? Frequently Asked Questions

What is tanstack-db?

Perfect for Real-Time Application Agents needing sub-millisecond queries and optimistic mutations with TanStack DB tanstack-db is a reactive client store for building local-first apps with features like typed collections and live queries.

How do I install tanstack-db?

Run the command: npx killer-skills add olegakbarov/ispo-code/tanstack-db. It works with Cursor, Windsurf, VS Code, Claude Code, and 19+ other IDEs.

What are the use cases for tanstack-db?

Key use cases include: Building real-time data visualizations with sub-millisecond queries, Implementing optimistic mutations for instant local writes, Syncing data in real-time across applications with TanStack DB.

Which IDEs are compatible with tanstack-db?

This skill is compatible with Cursor, Windsurf, VS Code, Trae, Claude Code, OpenClaw, Aider, Codex, OpenCode, Goose, Cline, Roo Code, Kiro, Augment Code, Continue, GitHub Copilot, Sourcegraph Cody, and Amazon Q Developer. Use the Killer-Skills CLI for universal one-command installation.

Are there any limitations for tanstack-db?

Currently in BETA. Requires understanding of reactive client stores and local-first app development.

How To Install

  1. 1. Open your terminal

    Open the terminal or command line in your project directory.

  2. 2. Run the install command

    Run: npx killer-skills add olegakbarov/ispo-code/tanstack-db. The CLI will automatically detect your IDE or AI agent and configure the skill.

  3. 3. Start using the skill

    The skill is now active. Your AI agent can use tanstack-db immediately in the current project.

Related Skills

Looking for an alternative to tanstack-db or another community skill for your workflow? Explore these related open-source skills.

View All

widget-generator

Logo of f
f

f.k.a. Awesome ChatGPT Prompts. Share, discover, and collect prompts from the community. Free and open source — self-host for your organization with complete privacy.

149.6k
0
AI

flags

Logo of vercel
vercel

flags is a Next.js feature management skill that enables developers to efficiently add or modify framework feature flags, streamlining React application development.

138.4k
0
Browser

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
AI

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
AI