TanStack logo

Skill

vue-db

build reactive databases with Vue 3

Covers Vue Database TanStack Frontend

Description

Vue 3 bindings for TanStack DB. useLiveQuery composable with MaybeRefOrGetter query functions, ComputedRef return values for all fields (data, state, collection, status, isLoading, isReady, isError). Dependency arrays with Vue refs. Conditional queries via returning undefined/null. Pre-created collection support via ref or getter. Import from @tanstack/vue-db (re-exports all of @tanstack/db).

SKILL.md

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

TanStack DB — Vue

Setup

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

// 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

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

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:

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

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:

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

Correct:

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.

© 2026 YourAI.tools. Every skill from an identity-verified publisher.

Independent catalog. Not affiliated with, endorsed by, or sponsored by Anthropic or any listed publisher. All trademarks belong to their respective owners.