npm install @tanstack/angular-db
npm install @tanstack/angular-db
See the Angular Functions Reference to see the full list of functions available in the Angular Adapter.
For comprehensive documentation on writing queries (filtering, joins, aggregations, etc.), see the Live Queries Guide.
The injectLiveQuery function creates a live query that automatically updates your component when data changes. It returns an object containing Angular signals for reactive state management:
import { Component } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
import { eq } from '@tanstack/db'
@Component({
selector: 'app-todo-list',
standalone: true,
template: `
@if (query.isLoading()) {
<div>Loading...</div>
} @else {
<ul>
@for (todo of query.data(); track todo.id) {
<li>{{ todo.text }}</li>
}
</ul>
}
`
})
export class TodoListComponent {
query = injectLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.completed, false))
.select(({ todos }) => ({ id: todos.id, text: todos.text }))
)
}
import { Component } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
import { eq } from '@tanstack/db'
@Component({
selector: 'app-todo-list',
standalone: true,
template: `
@if (query.isLoading()) {
<div>Loading...</div>
} @else {
<ul>
@for (todo of query.data(); track todo.id) {
<li>{{ todo.text }}</li>
}
</ul>
}
`
})
export class TodoListComponent {
query = injectLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.completed, false))
.select(({ todos }) => ({ id: todos.id, text: todos.text }))
)
}
Note: All return values (data, isLoading, status, etc.) are Angular signals, so call them with () in your template: query.data(), query.isLoading().
Template Syntax: Examples use Angular 17+ control flow (@if, @for). For Angular 16, use *ngIf and *ngFor instead.
For queries that depend on reactive values, use the params option to re-run the query when those values change:
import { Component, signal } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
import { gt } from '@tanstack/db'
@Component({
selector: 'app-filtered-todos',
standalone: true,
template: `
<div>{{ query.data().length }} high-priority todos</div>
`
})
export class FilteredTodosComponent {
minPriority = signal(5)
query = injectLiveQuery({
params: () => ({ minPriority: this.minPriority() }),
query: ({ params, q }) =>
q.from({ todos: todosCollection })
.where(({ todos }) => gt(todos.priority, params.minPriority))
})
}
import { Component, signal } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
import { gt } from '@tanstack/db'
@Component({
selector: 'app-filtered-todos',
standalone: true,
template: `
<div>{{ query.data().length }} high-priority todos</div>
`
})
export class FilteredTodosComponent {
minPriority = signal(5)
query = injectLiveQuery({
params: () => ({ minPriority: this.minPriority() }),
query: ({ params, q }) =>
q.from({ todos: todosCollection })
.where(({ todos }) => gt(todos.priority, params.minPriority))
})
}
Use the reactive params option when your query depends on:
When any reactive value accessed in the params function changes, the query is recreated and re-executed.
When a parameter value changes:
Use reactive params for dynamic queries:
import { Component, Input, signal } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
import { eq, and } from '@tanstack/db'
@Component({
selector: 'app-todo-list',
standalone: true,
template: `<div>{{ query.data().length }} todos</div>`
})
export class TodoListComponent {
// Angular 16+ compatible input
@Input({ required: true }) userId!: number
status = signal('active')
// Good - reactive params track all dependencies
query = injectLiveQuery({
params: () => ({
userId: this.userId,
status: this.status()
}),
query: ({ params, q }) =>
q.from({ todos: todosCollection })
.where(({ todos }) => and(
eq(todos.userId, params.userId),
eq(todos.status, params.status)
))
})
}
import { Component, Input, signal } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
import { eq, and } from '@tanstack/db'
@Component({
selector: 'app-todo-list',
standalone: true,
template: `<div>{{ query.data().length }} todos</div>`
})
export class TodoListComponent {
// Angular 16+ compatible input
@Input({ required: true }) userId!: number
status = signal('active')
// Good - reactive params track all dependencies
query = injectLiveQuery({
params: () => ({
userId: this.userId,
status: this.status()
}),
query: ({ params, q }) =>
q.from({ todos: todosCollection })
.where(({ todos }) => and(
eq(todos.userId, params.userId),
eq(todos.status, params.status)
))
})
}
Using Angular 17+ signal inputs:
import { Component, input, signal } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
import { eq, and } from '@tanstack/db'
@Component({
selector: 'app-todo-list',
standalone: true,
template: `<div>{{ query.data().length }} todos</div>`
})
export class TodoListComponent {
// Angular 17+ signal-based input
userId = input.required<number>()
status = signal('active')
query = injectLiveQuery({
params: () => ({
userId: this.userId(),
status: this.status()
}),
query: ({ params, q }) =>
q.from({ todos: todosCollection })
.where(({ todos }) => and(
eq(todos.userId, params.userId),
eq(todos.status, params.status)
))
})
}
import { Component, input, signal } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
import { eq, and } from '@tanstack/db'
@Component({
selector: 'app-todo-list',
standalone: true,
template: `<div>{{ query.data().length }} todos</div>`
})
export class TodoListComponent {
// Angular 17+ signal-based input
userId = input.required<number>()
status = signal('active')
query = injectLiveQuery({
params: () => ({
userId: this.userId(),
status: this.status()
}),
query: ({ params, q }) =>
q.from({ todos: todosCollection })
.where(({ todos }) => and(
eq(todos.userId, params.userId),
eq(todos.status, params.status)
))
})
}
Static queries don't need params:
import { Component } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
@Component({
selector: 'app-all-todos',
standalone: true,
template: `<div>{{ query.data().length }} todos</div>`
})
export class AllTodosComponent {
// No reactive dependencies - query never changes
query = injectLiveQuery((q) =>
q.from({ todos: todosCollection })
)
}
import { Component } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
@Component({
selector: 'app-all-todos',
standalone: true,
template: `<div>{{ query.data().length }} todos</div>`
})
export class AllTodosComponent {
// No reactive dependencies - query never changes
query = injectLiveQuery((q) =>
q.from({ todos: todosCollection })
)
}
Access multiple signals in template:
import { Component } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
import { eq } from '@tanstack/db'
@Component({
selector: 'app-todos',
standalone: true,
template: `
<div>Status: {{ query.status() }}</div>
<div>Loading: {{ query.isLoading() }}</div>
<div>Ready: {{ query.isReady() }}</div>
<div>Total: {{ query.data().length }}</div>
`
})
export class TodosComponent {
query = injectLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.completed, false))
)
}
import { Component } from '@angular/core'
import { injectLiveQuery } from '@tanstack/angular-db'
import { eq } from '@tanstack/db'
@Component({
selector: 'app-todos',
standalone: true,
template: `
<div>Status: {{ query.status() }}</div>
<div>Loading: {{ query.isLoading() }}</div>
<div>Ready: {{ query.isReady() }}</div>
<div>Total: {{ query.data().length }}</div>
`
})
export class TodosComponent {
query = injectLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.completed, false))
)
}
