원클릭으로
tanstack-db
Reactive client-first store for your API with collections, live queries, and optimistic mutations.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Reactive client-first store for your API with collections, live queries, and optimistic mutations.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Design engineering principles for making interfaces feel polished. Use when building UI components, reviewing frontend code, implementing animations, hover states, shadows, borders, typography, micro-interactions, enter/exit animations, or any visual detail work. Triggers on UI polish, design details, "make it feel better", "feels off", stagger animations, border radius, optical alignment, font smoothing, tabular numbers, image outlines, box shadows.
Project scaffolding CLI with 30+ integrations, custom templates, and MCP server for AI agents.
Opinionated toolkit for building, versioning, and publishing high-quality JavaScript/TypeScript packages.
Centralized, extensible devtools panel for TanStack libraries with a plugin architecture.
Framework-agnostic debouncing, throttling, rate limiting, queuing, and batching utilities.
Powerful asynchronous state management, server-state utilities, and data fetching for TS/JS, React, Vue, Solid, Svelte & Angular.
| name | tanstack-db |
| description | Reactive client-first store for your API with collections, live queries, and optimistic mutations. |
TanStack DB is a client-side embedded database layer built on differential dataflow. It maintains normalized collections, uses incremental computation for live queries, provides automatic optimistic mutations, and integrates with TanStack Query for data fetching. Sub-millisecond updates even with 100k+ rows.
Package: @tanstack/react-db
Query Integration: @tanstack/query-db-collection
Status: Beta (v0.5)
npm install @tanstack/react-db @tanstack/query-db-collection
import { createCollection } from "@tanstack/react-db";
import { queryCollectionOptions } from "@tanstack/query-db-collection";
const todoCollection = createCollection(
queryCollectionOptions({
queryKey: ["todos"],
queryFn: async () => api.todos.getAll(),
getKey: (item) => item.id,
schema: todoSchema,
onInsert: async ({ transaction }) => {
await Promise.all(
transaction.mutations.map((mutation) => api.todos.create(mutation.modified)),
);
},
onUpdate: async ({ transaction }) => {
await Promise.all(
transaction.mutations.map((mutation) => api.todos.update(mutation.modified)),
);
},
onDelete: async ({ transaction }) => {
await Promise.all(
transaction.mutations.map((mutation) => api.todos.delete(mutation.original.id)),
);
},
}),
);
// Eager (default): Load entire collection upfront. Best for <10k rows.
const smallCollection = createCollection(queryCollectionOptions({ syncMode: "eager" /* ... */ }));
// On-Demand: Load only what queries request. Best for >50k rows, search.
const largeCollection = createCollection(
queryCollectionOptions({
syncMode: "on-demand",
queryFn: async (ctx) => {
const params = parseLoadSubsetOptions(ctx.meta?.loadSubsetOptions);
return api.getProducts(params);
},
}),
);
// Progressive: Load query subset immediately, full sync in background.
const collaborativeCollection = createCollection(
queryCollectionOptions({ syncMode: "progressive" /* ... */ }),
);
import { useLiveQuery } from '@tanstack/react-db'
import { eq } from '@tanstack/db'
function TodoList() {
const { data: todos } = useLiveQuery((query) =>
query
.from({ todos: todoCollection })
.where(({ todos }) => eq(todos.completed, false))
)
return <ul>{todos.map(todo => <li key={todo.id}>{todo.text}</li>)}</ul>
}
const { data } = useLiveQuery((q) =>
q
.from({ t: todoCollection })
.where(({ t }) => eq(t.status, "active"))
.orderBy(({ t }) => t.createdAt, "desc")
.limit(10),
);
const { data } = useLiveQuery((q) =>
q
.from({ t: todoCollection })
.innerJoin({ u: userCollection }, ({ t, u }) => eq(t.userId, u.id))
.innerJoin({ p: projectCollection }, ({ u, p }) => eq(u.projectId, p.id))
.where(({ p }) => eq(p.id, currentProject.id)),
);
import { eq, lt, and } from "@tanstack/db";
// Equality
eq(field, value);
// Less than
lt(field, value);
// AND
and(eq(product.category, "electronics"), lt(product.price, 100));
const { data } = useLiveQuery((q) =>
q
.from({ product: productsCollection })
.where(({ product }) => and(eq(product.category, "electronics"), lt(product.price, 100)))
.orderBy(({ product }) => product.price, "asc")
.limit(10),
);
todoCollection.insert({
id: uuid(),
text: "New todo",
completed: false,
});
// Immediately: updates all live queries referencing this collection
// Background: calls onInsert handler to sync with server
// On failure: automatic rollback
| Before (TanStack Query only) | After (TanStack DB) |
|---|---|
Manual onMutate for optimistic state | Automatic |
Manual onError rollback logic | Automatic |
| Per-mutation cache invalidation | All live queries update automatically |
Live queries automatically generate optimized network requests:
// This live query...
useLiveQuery((q) =>
q
.from({ product: productsCollection })
.where(({ product }) => and(eq(product.category, "electronics"), lt(product.price, 100)))
.orderBy(({ product }) => product.price, "asc")
.limit(10),
);
// ...automatically generates:
// GET /api/products?category=electronics&price_lt=100&sort=price:asc&limit=10
queryFn: async (ctx) => {
const { filters, sorts, limit } = parseLoadSubsetOptions(ctx.meta?.loadSubsetOptions);
const params = new URLSearchParams();
filters.forEach(({ field, operator, value }) => {
if (operator === "eq") params.set(field.join("."), String(value));
else if (operator === "lt") params.set(`${field.join(".")}_lt`, String(value));
});
if (limit) params.set("limit", String(limit));
return fetch(`/api/products?${params}`).then((r) => r.json());
};
| Operation | Latency |
|---|---|
| Single row update (100k sorted collection) | ~0.7 ms |
| Subsequent queries (after sync) | <1 ms |
| Join across collections | Sub-millisecond |
import { createCollection, useLiveQuery } from "@tanstack/react-db";
import { queryCollectionOptions } from "@tanstack/query-db-collection";
import { eq, lt, and, parseLoadSubsetOptions } from "@tanstack/db";
eager (<10k), on-demand (>50k), progressive (collaborative)parseLoadSubsetOptions to map live query predicates to API params.filter().filter() in render instead of live query where clausesgetKey for proper normalizationonInsert, onUpdate, onDelete) for server sync