Angular integration for @tanstack/db
npm install @tanstack/angular-dbAngular hooks for TanStack DB. See TanStack/db for more details.
Installation
npm install @tanstack/angular-db @tanstack/db
Usage
Basic Setup
First, create a collection:
import { createCollection, localOnlyCollectionOptions } from "@tanstack/db"
interface Todo {
id: number
text: string
completed: boolean
projectID: number
created_at: Date
}
export const todosCollection = createCollection(
localOnlyCollectionOptions
getKey: (todo: Todo) => todo.id,
initialData: [
{
id: 1,
text: "Learn Angular",
completed: false,
projectID: 1,
created_at: new Date(),
},
],
})
)
Using injectLiveQuery in Components
Direct Collection Usage
The simplest way to use injectLiveQuery is to pass a collection directly:
import { Component } from "@angular/core"
import { injectLiveQuery } from "@tanstack/angular-db"
import { todosCollection } from "./collections/todos-collection"
@Component({
selector: "app-all-todos",
template: ,
@if (allTodos.isReady()) {
Total todos: {{ allTodos.data().length }}
@for (todo of allTodos.data(); track todo.id) {
{{ todo.text }}
}
} @else {
Loading todos...
}
})
export class AllTodosComponent {
// Direct collection usage - gets all items
allTodos = injectLiveQuery(todosCollection)
}
Static Query Functions
You can create filtered queries using a query function. Note: The query function is evaluated once and is not reactive to signal changes:
import { Component } from "@angular/core"
import { injectLiveQuery } from "@tanstack/angular-db"
import { eq } from "@tanstack/db"
import { todosCollection } from "./collections/todos-collection"
@Component({
selector: "app-todos",
template: ,
@if (todoQuery.isReady()) {
@for (todo of todoQuery.data(); track todo.id) {
{{ todo.text }}
}
} @else {
Loading todos...
}
})
export class TodosComponent {
// Static query - filters for incomplete todos
// This will not react to signal changes within the function
todoQuery = injectLiveQuery((q) =>
q
.from({ todo: todosCollection })
.where(({ todo }) => eq(todo.completed, false))
)
toggleTodo(id: number) {
todosCollection.utils.begin()
todosCollection.utils.write({
type: 'update',
key: id,
value: { completed: true }
})
todosCollection.utils.commit()
}
}
Reactive Queries with Parameters
For queries that need to react to component state changes, use the reactive parameters overload:
import { Component, signal } from "@angular/core"
import { injectLiveQuery } from "@tanstack/angular-db"
import { eq } from "@tanstack/db"
import { todosCollection } from "./collections/todos-collection"
@Component({
selector: "app-project-todos",
template:
@if (todoQuery.isReady()) {
// Reactive query - automatically recreates when selectedProjectId changes
todoQuery = injectLiveQuery({
params: () => ({ projectID: this.selectedProjectId() }),
query: ({ params, q }) =>
q
.from({ todo: todosCollection })
.where(({ todo }) => eq(todo.completed, false))
.where(({ todo }) => eq(todo.projectID, params.projectID)),
})
}
Advanced Configuration
You can also pass a full configuration object:
import { Component } from "@angular/core"
import { injectLiveQuery } from "@tanstack/angular-db"
import { eq } from "@tanstack/db"
import { todosCollection } from "./collections/todos-collection"
@Component({
selector: "app-configured-todos",
template: ,
@if (todoQuery.isReady()) {
@for (todo of todoQuery.data(); track todo.id) {
{{ todo.text }}
}
}
})
export class ConfiguredTodosComponent {
todoQuery = injectLiveQuery({
query: (q) =>
q
.from({ todo: todosCollection })
.where(({ todo }) => eq(todo.completed, false))
.select(({ todo }) => ({
id: todo.id,
text: todo.text,
})),
startSync: true,
gcTime: 5000,
})
}
Important Notes
Reactivity Behavior
- Direct collection: Automatically reactive to collection changes
- Static query function: Query is built once and is not reactive to signals read within the function
- Reactive parameters: Query rebuilds when any signal read in params() changes
- Collection configuration: Static, not reactive to external signals
Lifecycle Management
- injectLiveQuery automatically handles subscription cleanup when the component is destroyed
- Each call to injectLiveQuery creates a new collection instance (no caching/reuse)
- Collections are started immediately and will sync according to their configuration
Template Usage
Use Angular's new control flow syntax for best performance:
@if (query.isReady()) {
@for (item of query.data(); track item.id) {
API
injectLiveQuery()
Angular injection function for TanStack DB live queries. Must be called within an injection context (e.g., component constructor, inject(), or field initializer).
Overloads
// Direct collection - reactive to collection changes
function injectLiveQuery
collection: Collection
): LiveQueryResult
// Static query function - NOT reactive to signals within function
function injectLiveQuery
queryFn: (q: InitialQueryBuilder) => QueryBuilder
): LiveQueryResult
// Reactive query with parameters - recreates when params() signals change
function injectLiveQuery
params: () => TParams
query: (args: {
params: TParams
q: InitialQueryBuilder
}) => QueryBuilder
}): LiveQueryResult
// Collection configuration - static configuration
function injectLiveQuery
config: LiveQueryCollectionConfig
): LiveQueryResult
Returns
An object with Angular signals:
- data: Signal
- state: Signal
Parameters
- collection - Existing collection to observe directly
- queryFn - Function that builds a static query using the query builder
- options.params - Reactive function that returns parameters; triggers query rebuild when accessed signals change
- options.query - Function that builds a query using parameters and query builder
- config - Configuration object for creating a live query collection
Requirements
- Angular 16+ (requires signals support)
- Must be called within an Angular injection context
- Automatically handles cleanup when the injector is destroyed