# Tanstack DB Vue DB

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

- Skill: `tomevault-io/tanstack-db-vue-db` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/tanstack-db-vue-db`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/tanstack-db-vue-db/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/tanstack-db-vue-db

---


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

# TanStack DB — Vue

## Setup

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

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

<template>
  <div v-if="isLoading">Loading...</div>
  <ul v-else>
    <li v-for="todo in todos" :key="todo.id">{{ todo.text }}</li>
  </ul>
</template>
```

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

## Hook

### useLiveQuery

All return values are `ComputedRef`:

```ts
// Query function with reactive deps
const minPriority = ref(5)
const { data, isLoading, isReady, status } = useLiveQuery(
  (q) =>
    q
      .from({ todo: todoCollection })
      .where(({ todo }) => gt(todo.priority, minPriority.value)),
  [minPriority],
)

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

// Pre-created collection (reactive via ref)
const { data } = useLiveQuery(preloadedCollection)

// Conditional query
const userId = ref<number | null>(null)
const { data, status } = useLiveQuery(
  (q) => {
    if (!userId.value) return undefined
    return q
      .from({ todo: todoCollection })
      .where(({ todo }) => eq(todo.userId, userId.value))
  },
  [userId],
)
```

## Vue-Specific Patterns

### Reactive dependencies with refs

```ts
const filter = ref('active')
const { data } = useLiveQuery(
  (q) =>
    q
      .from({ todo: todoCollection })
      .where(({ todo }) => eq(todo.status, filter.value)),
  [filter],
)
// Query re-runs when filter.value changes
```

### Mutations in event handlers

```ts
const handleToggle = (id: number) => {
  todoCollection.update(id, (draft) => {
    draft.completed = !draft.completed
  })
}
```

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

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

const { data: projects } = 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>

<template>
  <div v-for="project in projects" :key="project.id">
    {{ project.name }}
    <IssueList :issues-collection="project.issues" />
  </div>
</template>
```

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

const props = defineProps<{ issuesCollection: Collection }>()
const { data: issues } = useLiveQuery(() => props.issuesCollection)
</script>

<template>
  <ul>
    <li v-for="issue in issues" :key="issue.id">{{ issue.title }}</li>
  </ul>
</template>
```

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

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

const { data: projects } = 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 Missing reactive deps in dependency array

Wrong:

```ts
const userId = ref(1)
const { data } = useLiveQuery((q) =>
  q
    .from({ todo: todoCollection })
    .where(({ todo }) => eq(todo.userId, userId.value)),
)
```

Correct:

```ts
const userId = ref(1)
const { data } = useLiveQuery(
  (q) =>
    q
      .from({ todo: todoCollection })
      .where(({ todo }) => eq(todo.userId, userId.value)),
  [userId],
)
```

Reactive refs used in the query must be included in the deps array for the query to re-run on changes.

Source: docs/framework/vue/overview.md

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

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

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/TanStack) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-16 -->

