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.
// Conditional query — return undefined/null to disable
const
useLiveQuery
(q) =>
if
return
undefined
return
from
todo
where
({ todo }) =>
eq
userId
// When disabled: status='disabled', data=undefined
useLiveSuspenseQuery
// data is ALWAYS defined — never undefined// Must wrap in <Suspense> and <ErrorBoundary>functionTodoList() {
const { data: todos } = useLiveSuspenseQuery((q) =>
q.from({ todo: todoCollection }),
)
return (
<ul>
{todos.map((t) => (
<likey={t.id}>{t.text}</li>
))}
</ul>
)
}
// With deps — re-suspends when deps changeconst { data } = useLiveSuspenseQuery(
(q) =>
q
.from({ todo: todoCollection })
.where(({ todo }) =>eq(todo.category, 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 },
[category],
)
// data is the flat array of all loaded pages// fetchNextPage() loads the next page// hasNextPage is true when more data is available
import { useLiveQueryEffect, eq } from'@octanejs/tanstack-db'// Fire side effects when rows enter, exit, or update a query result.// No render output — the effect is created on mount, disposed on unmount,// and recreated when deps change.functionChatComponent() {
useLiveQueryEffect(
{
query: (q) =>
q.from({ msg: messages }).where(({ msg }) =>eq(msg.role, 'user')),
skipInitial: true,
onEnter: async (event) => {
awaitgenerateResponse(event.value)
},
},
[],
)
return<div>...</div>
}
Includes (Hierarchical Data)
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:
// In route loader:await todoCollection.preload()
// In component — data available immediately:const { data } = useLiveQuery((q) => q.from({ todo: todoCollection }))
See meta-framework/SKILL.md for full preloading patterns.
Common Mistakes
CRITICAL Missing external values in dependency array
Wrong:
const { data } = useLiveQuery((q) =>
q.from({ todo: todoCollection }).where(({ todo }) =>eq(todo.userId, userId)),
)
Correct:
const { data } = useLiveQuery(
(q) =>
q
.from({ todo: todoCollection })
.where(({ todo }) =>eq(todo.userId, userId)),
[userId],
)
When the query uses external state not in the deps array, the query won't re-run when that value changes, showing stale results.
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 useLiveQuery throws InvalidSourceError: The value provided for alias "todo" is not a Collection, it usually means two copies of @tanstack/db are installed. The collection was created by one copy, but useLiveQuery checks instanceof against the other.
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.