# Svelte DB

> Svelte 5 bindings for TanStack DB. useLiveQuery with Svelte 5 runes ($state, $derived, $effect). Dependency arrays use getter functions (() => value) for reactive props. Direct destructuring breaks reactivity — use dot notation or wrap with $derived. Conditional queries via returning undefined/null. Import from @tanstack/svelte-db (re-exports all of @tanstack/db).

- Skill: `tanstack/svelte-db` (Agent Skill)
- Install (CLI): `npx skillmds@latest add tanstack/svelte-db`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tanstack/svelte-db/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: TanStack (https://skillmd.com/u/tanstack)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tanstack/svelte-db

---


This skill builds on db-core. Read it first for collection setup, query builder, and mutation patterns.

# TanStack DB — Svelte 5

## Setup

```svelte
<script lang="ts">
import { useLiveQuery, eq, not } from "@tanstack/svelte-db"

const todosQuery = useLiveQuery((q) =>
  q
    .from({ todo: todoCollection })
    .where(({ todo }) => not(todo.completed))
    .orderBy(({ todo }) => todo.created_at, "asc")
)
</script>

{#if todosQuery.isLoading}
  <div>Loading...</div>
{:else}
  <ul>
    {#each todosQuery.data as todo (todo.id)}
      <li>{todo.text}</li>
    {/each}
  </ul>
{/if}
```

`@tanstack/svelte-db` re-exports everything from `@tanstack/db`.

## Hook

### useLiveQuery

Returns a reactive object with getter properties:

```ts
// Query function — access via dot notation
const query = useLiveQuery((q) => q.from({ todo: todoCollection }))
// query.data, query.isLoading, query.status, etc.

// With reactive deps — use GETTER FUNCTIONS
let minPriority = $state(5)
const query = useLiveQuery(
  (q) =>
    q
      .from({ todo: todoCollection })
      .where(({ todo }) => gt(todo.priority, minPriority)),
  [() => minPriority],
)

// Config object
const query = useLiveQuery({
  query: (q) => q.from({ todo: todoCollection }),
  gcTime: 60000,
})

// Pre-created collection
const query = useLiveQuery(preloadedCollection)
```

## Svelte-Specific Patterns

### Destructuring with $derived

```ts
// WRONG — breaks reactivity
const { data, isLoading } = useLiveQuery(...)

// CORRECT — use dot notation
const query = useLiveQuery(...)
// Access: query.data, query.isLoading

// CORRECT — wrap with $derived if you need destructuring
const query = useLiveQuery(...)
const { data, isLoading } = $derived(query)
```

### Props in dependency arrays

```svelte
<script lang="ts">
  let { userId }: { userId: number } = $props()

  const todosQuery = useLiveQuery(
    (q) => q.from({ todo: todoCollection })
             .where(({ todo }) => eq(todo.userId, userId)),
    [() => userId]
  )
</script>
```

## 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:

```svelte
<!-- ProjectList.svelte -->
<script lang="ts">
import { useLiveQuery, eq } from "@tanstack/svelte-db"
import IssueList from "./IssueList.svelte"

const projectsQuery = useLiveQuery((q) =>
  q.from({ p: projectsCollection }).select(({ p }) => ({
    id: p.id,
    name: p.name,
    issues: q
      .from({ i: issuesCollection })
      .where(({ i }) => eq(i.projectId, p.id))
      .select(({ i }) => ({ id: i.id, title: i.title })),
  }))
)
</script>

{#each projectsQuery.data as project (project.id)}
  <div>
    {project.name}
    <IssueList issuesCollection={project.issues} />
  </div>
{/each}
```

```svelte
<!-- IssueList.svelte — subscribes to the child Collection -->
<script lang="ts">
import { useLiveQuery } from "@tanstack/svelte-db"

let { issuesCollection } = $props()
const issuesQuery = useLiveQuery(issuesCollection)
</script>

{#each issuesQuery.data as issue (issue.id)}
  <li>{issue.title}</li>
{/each}
```

With `toArray()`, child results are plain arrays and the parent re-emits on child changes:

```ts
import { toArray, eq } from '@tanstack/svelte-db'

const projectsQuery = useLiveQuery((q) =>
  q.from({ p: projectsCollection }).select(({ p }) => ({
    id: p.id,
    name: p.name,
    issues: toArray(
      q
        .from({ i: issuesCollection })
        .where(({ i }) => eq(i.projectId, p.id))
        .select(({ i }) => ({ id: i.id, title: i.title })),
    ),
  })),
)
// project.issues is a plain array — no subcomponent needed
```

See db-core/live-queries/SKILL.md for full includes rules (correlation conditions, nested includes, aggregates).

## Common Mistakes

### CRITICAL Passing values instead of getter functions in deps

Wrong:

```ts
let filter = $state('active')
const query = useLiveQuery(
  (q) =>
    q
      .from({ todo: todoCollection })
      .where(({ todo }) => eq(todo.status, filter)),
  [filter],
)
```

Correct:

```ts
let filter = $state('active')
const query = useLiveQuery(
  (q) =>
    q
      .from({ todo: todoCollection })
      .where(({ todo }) => eq(todo.status, filter)),
  [() => filter],
)
```

In Svelte 5, deps must be getter functions. Passing values directly captures them at creation time — changes won't trigger re-execution.

Source: docs/framework/svelte/overview.md

### HIGH Direct destructuring breaks reactivity

Wrong:

```ts
const { data, isLoading } = useLiveQuery((q) =>
  q.from({ todo: todoCollection }),
)
```

Correct:

```ts
const query = useLiveQuery((q) => q.from({ todo: todoCollection }))
// Use query.data, query.isLoading
```

Direct destructuring captures values at creation time. Use dot notation or wrap with `$derived`.

Source: packages/svelte-db/src/useLiveQuery.svelte.ts

See also: db-core/live-queries/SKILL.md — for query builder API.

See also: db-core/mutations-optimistic/SKILL.md — for mutation patterns.

