npm install @tanstack/svelte-db
npm install @tanstack/svelte-db
See the Svelte Functions Reference to see the full list of hooks available in the Svelte Adapter.
For comprehensive documentation on writing queries (filtering, joins, aggregations, etc.), see the Live Queries Guide.
The useLiveQuery hook creates a live query that automatically updates your component when data changes:
import { useLiveQuery } from '@tanstack/svelte-db'
function TodoList() {
const { data, isLoading } = useLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.completed, false))
.select(({ todos }) => ({ id: todos.id, text: todos.text }))
)
if (isLoading) return <div>Loading...</div>
return (
<ul>
{data.map(todo => <li key={todo.id}>{todo.text}</li>)}
</ul>
)
}
import { useLiveQuery } from '@tanstack/svelte-db'
function TodoList() {
const { data, isLoading } = useLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.completed, false))
.select(({ todos }) => ({ id: todos.id, text: todos.text }))
)
if (isLoading) return <div>Loading...</div>
return (
<ul>
{data.map(todo => <li key={todo.id}>{todo.text}</li>)}
</ul>
)
}
All query hooks (useLiveQuery, useLiveInfiniteQuery, useLiveSuspenseQuery) accept an optional dependency array as their last parameter. This array works similarly to Svelte's useEffect dependencies - when any value in the array changes, the query is recreated and re-executed.
Use dependency arrays when your query depends on external svelteive values (props, state, or other hooks):
function FilteredTodos({ minPriority }: { minPriority: number }) {
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => gt(todos.priority, minPriority)),
[minPriority] // Re-run when minPriority changes
)
return <div>{data.length} high-priority todos</div>
}
function FilteredTodos({ minPriority }: { minPriority: number }) {
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => gt(todos.priority, minPriority)),
[minPriority] // Re-run when minPriority changes
)
return <div>{data.length} high-priority todos</div>
}
When a dependency value changes:
Include all external values used in the query:
// Good - all external values in deps
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => and(
eq(todos.userId, userId),
eq(todos.status, status)
)),
[userId, status]
)
// Bad - missing dependencies
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.userId, userId)),
[] // Missing userId!
)
// Good - all external values in deps
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => and(
eq(todos.userId, userId),
eq(todos.status, status)
)),
[userId, status]
)
// Bad - missing dependencies
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.userId, userId)),
[] // Missing userId!
)
Empty array for static queries:
// No external dependencies - query never changes
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection }),
[]
)
// No external dependencies - query never changes
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection }),
[]
)
Omit the array for queries with no external dependencies:
// Same as above - no deps needed
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
)
// Same as above - no deps needed
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
)
For paginated data with live updates, use useLiveInfiniteQuery:
const { data, pages, fetchNextPage, hasNextPage } = useLiveInfiniteQuery(
(q) => q
.from({ posts: postsCollection })
.where(({ posts }) => eq(posts.category, category))
.orderBy(({ posts }) => posts.createdAt, 'desc'),
{
pageSize: 20,
getNextPageParam: (lastPage, allPages) =>
lastPage.length === 20 ? allPages.length : undefined
},
[category] // Re-run when category changes
)
const { data, pages, fetchNextPage, hasNextPage } = useLiveInfiniteQuery(
(q) => q
.from({ posts: postsCollection })
.where(({ posts }) => eq(posts.category, category))
.orderBy(({ posts }) => posts.createdAt, 'desc'),
{
pageSize: 20,
getNextPageParam: (lastPage, allPages) =>
lastPage.length === 20 ? allPages.length : undefined
},
[category] // Re-run when category changes
)
Note: The dependency array is only available when using the query function variant, not when passing a pre-created collection.
For Svelte Suspense integration, use useLiveSuspenseQuery:
function TodoList({ filter }: { filter: string }) {
const { data } = useLiveSuspenseQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.filter, filter)),
[filter] // Re-suspends when filter changes
)
return (
<ul>
{data.map(todo => <li key={todo.id}>{todo.text}</li>)}
</ul>
)
}
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<TodoList filter="active" />
</Suspense>
)
}
function TodoList({ filter }: { filter: string }) {
const { data } = useLiveSuspenseQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.filter, filter)),
[filter] // Re-suspends when filter changes
)
return (
<ul>
{data.map(todo => <li key={todo.id}>{todo.text}</li>)}
</ul>
)
}
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<TodoList filter="active" />
</Suspense>
)
}
When dependencies change, useLiveSuspenseQuery will re-suspend, showing your Suspense fallback until the new data is ready.
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.
