npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tanstack/angular-db

v0.1.61

Published

Angular integration for @tanstack/db

Readme

@tanstack/angular-db

Angular 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<Todo>({
    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()) {
      <div>Total todos: {{ allTodos.data().length }}</div>
      @for (todo of allTodos.data(); track todo.id) {
        <div>{{ todo.text }}</div>
      }
    } @else {
      <div>Loading todos...</div>
    }
  `,
})
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) {
        <div class="todo-item">
          {{ todo.text }}
          <button (click)="toggleTodo(todo.id)">
            {{ todo.completed ? "Undo" : "Complete" }}
          </button>
        </div>
      }
    } @else {
      <div>Loading todos...</div>
    }
  `,
})
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: `
    <select (change)="selectedProjectId.set(+$any($event).target.value)">
      <option [value]="1">Project 1</option>
      <option [value]="2">Project 2</option>
      <option [value]="3">Project 3</option>
    </select>

    @if (todoQuery.isReady()) {
      <div>Todos for project {{ selectedProjectId() }}:</div>
      @for (todo of todoQuery.data(); track todo.id) {
        <div class="todo-item">
          {{ todo.text }}
        </div>
      }
    } @else {
      <div>Loading todos...</div>
    }
  `,
})
export class ProjectTodosComponent {
  selectedProjectId = signal(1)

  // 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) {
        <div>{{ todo.text }}</div>
      }
    }
  `,
})
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) {
    <div>{{ item.text }}</div>
  }
} @else if (query.isError()) {
  <div>Error loading data</div>
} @else {
  <div>Loading...</div>
}

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<TResult, TKey, TUtils>(
  collection: Collection<TResult, TKey, TUtils>
): LiveQueryResult<TResult>

// Static query function - NOT reactive to signals within function
function injectLiveQuery<TContext>(
  queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>
): LiveQueryResult<TContext>

// Reactive query with parameters - recreates when params() signals change
function injectLiveQuery<TContext, TParams>(options: {
  params: () => TParams
  query: (args: {
    params: TParams
    q: InitialQueryBuilder
  }) => QueryBuilder<TContext>
}): LiveQueryResult<TContext>

// Collection configuration - static configuration
function injectLiveQuery<TContext>(
  config: LiveQueryCollectionConfig<TContext>
): LiveQueryResult<TContext>

Returns

An object with Angular signals:

  • data: Signal<Array> - Array of query results, automatically updates
  • state: Signal<Map<Key, T>> - Map of results by key, automatically updates
  • collection: Signal - The underlying collection instance
  • status: Signal - Current status ('idle' | 'loading' | 'ready' | 'error' | 'cleaned-up')
  • isLoading: Signal - true when status is 'loading'
  • isReady: Signal - true when status is 'ready'
  • isIdle: Signal - true when status is 'idle'
  • isError: Signal - true when status is 'error'
  • isCleanedUp: Signal - true when status is 'cleaned-up'

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