React bindings for TanStack DB. Prefer useLiveQuery({ query }) with derived structured query identity. Provide queryKey only for opaque functional query variants or very hot render paths. Dependency arrays are legacy and warn before 1.0 removal. useLiveSuspenseQuery for React Suspense with Error Boundaries (data always defined). useLiveInfiniteQuery for cursor-based pagination (pageSize, fetchNextPage, hasNextPage, isFetchingNextPage). usePacedMutations for debounced React state updates. Return shape: data, state, collection, status, isLoading, isReady, isError. Import from @tanstack/react-db (re-exports all of @tanstack/db).
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
The command stays on one line. Scroll horizontally to inspect it before copying.
Prefer a local copy? Download the files currently available to SkillsMP.
Showing SKILL.md
SKILL.md
Source instructions · Read-only preview
name
react-db
description
React bindings for TanStack DB. Prefer useLiveQuery({ query }) with derived structured query identity. Provide queryKey only for opaque functional query variants or very hot render paths. Dependency arrays are legacy and warn before 1.0 removal. useLiveSuspenseQuery for React Suspense with Error Boundaries (data always defined). useLiveInfiniteQuery for cursor-based pagination (pageSize, fetchNextPage, hasNextPage, isFetchingNextPage). usePacedMutations for debounced React state updates. Return shape: data, state, collection, status, isLoading, isReady, isError. Import from @tanstack/react-db (re-exports all of @tanstack/db).
// When disabled: status='disabled', data=undefined
useLiveSuspenseQuery
// data is ALWAYS defined — never undefined// Must wrap in <Suspense> and <ErrorBoundary>functionTodoList() {
const { data: todos } = useLiveSuspenseQuery({
query: (q) => q.from({ todo: todoCollection }),
})
return (
<ul>
{todos.map((t) => (
<likey={t.id}>{t.text}</li>
))}
</ul>
)
}
// Structured captured values are part of the derived identity and re-suspend when changedconst { data } = useLiveSuspenseQuery({
query: (q) =>
q
.from({ todo: todoCollection })
.where(({ todo }) =>eq(todo.category, category)),
})
useLiveInfiniteQuery
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useLiveInfiniteQuery(
(q) =>
q
.from({ posts: postsCollection })
.where(({ posts }) =>eq(posts.category, category))
.orderBy(({ posts }) => posts.createdAt, 'desc'),
{
pageSize: 20,
},
)
// data is the flat array of all loaded pages// fetchNextPage() loads the next page// hasNextPage is true when more data is available
When a query uses includes (subqueries in select), each child field is a live Collection by default. Subscribe to it with useLiveQuery in a subcomponent:
See db-core/live-queries/SKILL.md for full includes rules (correlation conditions, nested includes, aggregates).
Virtual Properties
Live query results include computed, read-only virtual properties on every row:
$synced: true when no pending local optimistic write affects the row;
false while one does. It does not prove backend confirmation.
$origin: "local" if the last confirmed change came from this client, otherwise "remote".
$key: the row key for the result.
$collectionId: the source collection ID.
These props are added automatically and can be used in where, select, and orderBy clauses. Do not persist them back to storage.
const { data } = useLiveQuery({
query: (q) =>
q
.from({ todo: todoCollection })
.where(({ todo }) =>eq(todo.$synced, false)),
})
// Shows rows with pending local optimistic writes
React-Specific Patterns
Query identity
// Structured captured values are included in the derived identityconst { data } = useLiveQuery({
query: (q) =>
q
.from({ todo: todoCollection })
.where(({ todo }) =>and(eq(todo.userId, userId), eq(todo.status, filter)),
),
})
// Static queryconst { data } = useLiveQuery({
query: (q) => q.from({ todo: todoCollection }),
})
Use queryKey only when DB cannot derive identity from structured IR, such as
.fn.where, .fn.select, .fn.having, or as a deliberate performance escape
hatch on a hot render path:
Before 1.0, opaque IR warns and keeps legacy mount-stable identity. Slow or
repeated derived identity work also warns once. Both point to the same
queryKey escape hatch; unhashable IR without a key will throw in 1.0.
// In route loader:await todoCollection.preload()
// In component — data available immediately:const { data } = useLiveQuery({
query: (q) => q.from({ todo: todoCollection }),
})
See meta-framework/SKILL.md for full preloading patterns.
Common Mistakes
CRITICAL Using opaque query logic without queryKey
Structured expressions are hashable by default. Functional query variants are
opaque runtime code, so they need an explicit key to say when identity changes.
useLiveSuspenseQuery throws errors during rendering. Without an Error Boundary, the entire app crashes.
Source: docs/guides/live-queries.md
HIGH "Not a Collection" error from duplicate @tanstack/db
If a query-builder alias throws
InvalidSourceError: The value provided for alias "todo" is not a Collection,
it can mean two copies of @tanstack/db are installed. Direct
useLiveQuery(preCreatedCollection) detection is structural and works across
package copies or realms, but q.from({ todo: collection }) still validates
the source with the core collection class.
In dev mode, TanStack DB also throws DuplicateDbInstanceError if two instances are detected.
The root cause is typically a dependency that bundles its own copy instead of declaring @tanstack/db as a peerDependency.
HIGH Tension: Query expressiveness vs. IVM constraints
The query builder looks like SQL but has constraints that SQL doesn't — equality joins only, orderBy required for limit/offset, no distinct without select. Agents write SQL-style queries that violate these constraints. See db-core/live-queries/SKILL.md § Common Mistakes for all constraints.
See also: db-core/live-queries/SKILL.md — for query builder API and all operators.
See also: db-core/mutations-optimistic/SKILL.md — for mutation patterns.
See also: meta-framework/SKILL.md — for preloading in route loaders.