Skip to main content

Reference

injectStore

function injectStore<TState, TSelected = NoInfer<TState>>(
  store: Atom<TState> | ReadonlyAtom<TState>,
  selector?: (state: NoInfer<TState>) => TSelected,
  options?: CreateSignalOptions<TSelected> & { injector?: Injector }
): Signal<TSelected>
An Angular injection function that subscribes to a TanStack Store atom and returns a readonly signal. The signal will update when the selected state changes according to the equality function.
store
Atom<TState> | ReadonlyAtom<TState>
The store atom to subscribe to.
selector
(state: TState) => TSelected
default:"(d) => d"
Optional function that selects a slice of state from the atom. Defaults to returning the entire state.
options
CreateSignalOptions<TSelected> & { injector?: Injector }
Optional configuration object.

Returns

Signal<TSelected>
Signal<TSelected>
An Angular readonly signal containing the selected state value.

Usage

Basic Usage

import { Component } from '@angular/core'
import { injectStore } from '@tanstack/angular-store'
import { store } from './store'

@Component({
  selector: 'app-counter',
  template: `<div>Count: {{ count() }}</div>`,
  standalone: true
})
export class CounterComponent {
  count = injectStore(store, (state) => state.count)
}

Selecting Multiple Values

import { Component } from '@angular/core'
import { injectStore } from '@tanstack/angular-store'
import { store } from './store'

@Component({
  selector: 'app-user-profile',
  template: `
    <div>
      <h2>{{ user().name }}</h2>
      <p>{{ user().email }}</p>
      <span>{{ user().role }}</span>
    </div>
  `,
  standalone: true
})
export class UserProfileComponent {
  user = injectStore(store, (state) => ({
    name: state.user.name,
    email: state.user.email,
    role: state.user.role
  }))
}

Custom Equality Function

import { Component } from '@angular/core'
import { injectStore } from '@tanstack/angular-store'
import { store } from './store'

@Component({
  selector: 'app-settings',
  template: `<div>{{ settings() | json }}</div>`,
  standalone: true
})
export class SettingsComponent {
  settings = injectStore(
    store,
    (state) => state.settings,
    {
      equal: (a, b) => JSON.stringify(a) === JSON.stringify(b)
    }
  )
}

Using Entire State

import { Component } from '@angular/core'
import { injectStore } from '@tanstack/angular-store'
import { store } from './store'

@Component({
  selector: 'app-state-viewer',
  template: `
    <div>
      <p>Count: {{ state().count }}</p>
      <p>Name: {{ state().name }}</p>
      <p>Active: {{ state().isActive }}</p>
    </div>
  `,
  standalone: true
})
export class StateViewerComponent {
  state = injectStore(store)
}

With Computed Signals

import { Component, computed } from '@angular/core'
import { injectStore } from '@tanstack/angular-store'
import { store } from './store'

@Component({
  selector: 'app-todo-stats',
  template: `
    <div>
      <p>Total: {{ stats().total }}</p>
      <p>Completed: {{ stats().completed }}</p>
      <p>Pending: {{ stats().pending }}</p>
    </div>
  `,
  standalone: true
})
export class TodoStatsComponent {
  todos = injectStore(store, (state) => state.todos)
  
  stats = computed(() => {
    const all = this.todos()
    return {
      total: all.length,
      completed: all.filter(t => t.done).length,
      pending: all.filter(t => !t.done).length
    }
  })
}

With Custom Injector

import { Component, Injector, inject } from '@angular/core'
import { injectStore } from '@tanstack/angular-store'
import { store } from './store'

@Component({
  selector: 'app-custom',
  template: `<div>{{ data() }}</div>`,
  standalone: true
})
export class CustomComponent {
  private injector = inject(Injector)
  
  // Use custom injector for dynamic injection
  data = injectStore(
    store,
    (state) => state.data,
    { injector: this.injector }
  )
}

Control Flow with @if

import { Component } from '@angular/core'
import { injectStore } from '@tanstack/angular-store'
import { store } from './store'

@Component({
  selector: 'app-conditional',
  template: `
    @if (isLoggedIn()) {
      <div>Welcome, {{ user().name }}!</div>
    } @else {
      <div>Please log in</div>
    }
  `,
  standalone: true
})
export class ConditionalComponent {
  isLoggedIn = injectStore(store, (state) => state.isLoggedIn)
  user = injectStore(store, (state) => state.user)
}

With @for Loop

import { Component } from '@angular/core'
import { injectStore } from '@tanstack/angular-store'
import { store } from './store'

@Component({
  selector: 'app-todo-list',
  template: `
    <ul>
      @for (todo of todos(); track todo.id) {
        <li>{{ todo.text }}</li>
      }
    </ul>
  `,
  standalone: true
})
export class TodoListComponent {
  todos = injectStore(store, (state) => state.todos)
}

Notes

  • Must be called within an injection context (constructor, field initializer) or provide a custom injector
  • Returns an Angular readonly signal - access the value by calling it as a function
  • Uses linkedSignal internally for optimal Angular reactivity
  • Automatically cleans up subscriptions using DestroyRef
  • Defaults to shallow equality comparison
  • Compatible with Angular’s signals and computed values
  • Works seamlessly with Angular’s control flow syntax (@if, @for, etc.)