Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
agents-inc avatar

Web State Ngrx Signalstore

  • 9 installs
  • 19 repo stars
  • Updated July 19, 2026
  • agents-inc/skills

web-state-ngrx-signalstore is a Claude Code skill for Angular Signals-based client state using NgRx SignalStore.

About

web-state-ngrx-signalstore is a Claude Code skill for reactive client-state management in Angular 17+ with NgRx SignalStore. A developer uses it to compose stores functionally instead of using traditional NgRx actions, reducers, and effects. It covers signalStore with withState, withComputed, and withMethods, immutable patchState updates, withEntities for collections, and rxMethod for RxJS side effects.

  • Angular Signals state via signalStore with withState/withComputed/withMethods
  • Immutable patchState updates and withEntities for collections
  • rxMethod RxJS integration and custom signalStoreFeature composition

Web State Ngrx Signalstore by the numbers

  • 9 all-time installs (skills.sh)
  • Ranked #1,714 of 2,245 Frontend Development skills by installs in the Skillselion catalog
  • Data as of Aug 1, 2026 (Skillselion catalog sync)
At a glance

web-state-ngrx-signalstore capabilities & compatibility

Capabilities
client state management · entity management · computed state · rxjs effects
Use cases
frontend
Pricing
Free
From the docs

What web-state-ngrx-signalstore says it does

Use NgRx SignalStore for reactive client state in Angular 17+.
SKILL.md
You MUST use `patchState()` for ALL state updates - NEVER mutate state directly
SKILL.md
You MUST wrap async operations in `rxMethod()` from `@ngrx/signals/rxjs-interop` for RxJS integration
SKILL.md
npx skills add https://github.com/agents-inc/skills --skill web-state-ngrx-signalstore

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs9
repo stars19
Last updatedJuly 19, 2026
Repositoryagents-inc/skills

What it does

Manage Angular 17+ client state with NgRx SignalStore, composable features, entities, and RxJS effects.

Who is it for?

Composable, reusable Angular 17+ client-state features with fine-grained Signals reactivity

Skip if: Server/API data as the primary source, simple component-local state, or Angular below 17

When should I use this skill?

Managing Angular state with Signals and SignalStore

What you get

Composable SignalStore features with immutable patchState updates, entities, and RxJS-integrated effects.

  • signalStore with withState/withComputed/withMethods
  • withEntities collection management
  • rxMethod RxJS effects and custom signalStoreFeature

By the numbers

  • 7 example files (core, entities, effects, features, testing, migration, plus reference)

Files

SKILL.mdMarkdownGitHub ↗

NgRx SignalStore Patterns

Quick Guide: Use NgRx SignalStore for reactive client state in Angular 17+. Compose stores with withState, withComputed, withMethods. Use patchState for immutable updates. Use withEntities for collections. NEVER use traditional NgRx patterns (actions, reducers, effects) in new SignalStore code.

Detailed Resources:

  • examples/core.md - signalStore, withState, withComputed, withMethods, withProps
  • examples/entities.md - withEntities, CRUD operations, prependEntity/upsertEntity (v20+)
  • examples/effects.md - rxMethod, signalMethod (v19+), side effects
  • examples/features.md - signalStoreFeature, custom features, DevTools
  • examples/testing.md - Unit tests, unprotected(), mocking strategies
  • examples/migration.md - Migration from traditional NgRx
  • reference.md - Decision frameworks, anti-patterns, red flags

---

<critical_requirements>

CRITICAL: Before Managing State with NgRx SignalStore

(You MUST use `patchState()` for ALL state updates - NEVER mutate state directly)

(You MUST wrap async operations in `rxMethod()` from `@ngrx/signals/rxjs-interop` for RxJS integration)

(You MUST use `withEntities()` from `@ngrx/signals/entities` for entity collections - NOT arrays in state)

(You MUST use named exports ONLY - NO default exports in any store files)

(You MUST use named constants for ALL numbers - NO magic numbers in state code)

</critical_requirements>

---

Auto-detection: NgRx SignalStore, signalStore, withState, withComputed, withMethods, patchState, withEntities, rxMethod, signalStoreFeature, @ngrx/signals

When to use:

  • Managing reactive client state in Angular 17+ applications
  • Building composable, reusable store features
  • Handling entity collections with CRUD operations
  • Integrating RxJS operators for side effects
  • Applications requiring fine-grained reactivity with Angular Signals

Key patterns covered:

  • Store creation with signalStore() and providedIn options
  • State, computed, and methods composition
  • Entity management with withEntities
  • RxJS integration with rxMethod and signal-only signalMethod
  • Custom features with signalStoreFeature and context-aware withFeature (v20+)
  • Derived reactive state with withLinkedState (v20+)
  • Call state patterns (loading, loaded, error)

When NOT to use:

  • Server/API data as primary source (use HTTP services with rxMethod for caching)
  • Simple component-local state (use Angular signals directly)
  • Projects still on Angular < 17 (requires signals)
  • Teams unfamiliar with functional composition patterns

---

<philosophy>

Philosophy

NgRx SignalStore is a lightweight, functional state management solution built on Angular Signals. It replaces traditional NgRx patterns (actions, reducers, effects, selectors) with a composable, feature-based approach that eliminates boilerplate while maintaining predictability.

Core Principles:

1. Functional Composition - Build stores by composing features like withState, withComputed, withMethods 2. Signal-Based Reactivity - Leverage Angular's fine-grained reactivity for optimal performance 3. Immutable Updates - Use patchState() for predictable state transitions 4. Extensibility - Create custom features with signalStoreFeature() for reuse across stores

Key Architecture Decisions:

1. `signalStore()` creates a fully typed store as an injectable Angular service 2. `patchState()` ensures immutable updates without Immer dependency 3. `withEntities()` provides standardized entity management (normalized ids/entityMap structure with built-in CRUD updaters) 4. `rxMethod()` bridges Angular Signals with RxJS for complex async flows 5. Protected state (v18+) prevents external mutations by default; v19+ applies deep freeze recursively

State Ownership:

State TypeSolutionReason
Server/API dataHTTP services + rxMethodCaching in store, fetch via services
Shared client stateSignalStoreReactivity, composition, DevTools
Component-local stateAngular signalsSimpler, no overhead
URL state (filters)Router query paramsShareable, bookmarkable

</philosophy>

---

<patterns>

Core Patterns

Pattern 1: Basic Store with signalStore

Create stores using signalStore() with withState, withComputed, and withMethods.

Feature Ordering

Features execute in order. State features must come first:

1. withState() - Define state 2. withComputed() - Derived values from state 3. withMethods() - Actions that update state 4. withHooks() - Lifecycle hooks (onInit, onDestroy)

For implementation examples, see examples/core.md.

---

Pattern 2: State Updates with patchState

Use patchState() for all state modifications. It ensures immutability and proper signal notifications.

When to Use
  • Synchronous state updates
  • Partial state updates (spread not needed)
  • Inside withMethods() and custom features
Key Behaviors
  • Accepts partial state object or updater functions
  • Multiple updaters can be passed to single call
  • Works with entity updaters from @ngrx/signals/entities

For implementation examples, see examples/core.md.

---

Pattern 3: Entity Management with withEntities

Use withEntities() for collections of items with IDs. Provides standardized CRUD operations and efficient lookups.

When to Use
  • Lists of items with unique identifiers
  • CRUD operations on collections
  • Need for efficient ID-based lookups
  • Multiple entity collections in one store
Available Updaters
  • setAllEntities() - Replace all entities
  • addEntity() / addEntities() - Add new entities
  • setEntity() / setEntities() - Upsert entities
  • updateEntity() / updateEntities() - Partial updates
  • removeEntity() / removeEntities() - Delete entities

For implementation examples, see examples/entities.md.

---

Pattern 4: RxJS Integration with rxMethod

Use rxMethod() for side effects that need RxJS operators (debounce, switchMap, etc.).

When to Use
  • Debounced search/filtering
  • Cancellable HTTP requests
  • Complex async flows with multiple operators
  • Reactive streams from signals
Key Behaviors
  • Accepts Observable<T>, Signal<T>, or T as input
  • Factory function receives Observable<T> for piping
  • Runs in injection context (can use inject())
  • Auto-unsubscribes on store destroy

For implementation examples, see examples/effects.md.

---

Pattern 4b: Signal-Only Side Effects with signalMethod (v19+)

Use signalMethod() for side effects that don't need RxJS operators.

When to Use
  • Simple side effects without RxJS dependency
  • When you want to work purely with Signals
  • Side effects that don't require debounce/switchMap/cancellation
  • When injection context is not available
Key Behaviors
  • Accepts Signal<T> or T as input (no Observable)
  • Processor function runs independently of injection context
  • Only parameter signals are tracked; internal signals remain untracked
  • You must handle race conditions manually (no built-in switchMap)
rxMethod vs signalMethod
FeaturerxMethodsignalMethod
RxJS requiredYesNo
Operators (debounce, switchMap)YesNo
Race condition handlingBuilt-inManual
Input typesT, Signal<T>, Observable<T>T, Signal<T>

For implementation examples, see examples/effects.md.

---

Pattern 5: Custom Features with signalStoreFeature

Use signalStoreFeature() to create reusable store functionality.

When to Use
  • Shared patterns across multiple stores (loading state, error handling)
  • Encapsulated feature modules
  • Third-party store extensions
  • DRY principle for repeated store logic
Type Constraints

Use type() helper to specify required store members:

  • Ensures feature is only used with compatible stores
  • Provides full type inference
  • Enables composition of features

For implementation examples, see examples/features.md.

---

Pattern 6: Lifecycle Hooks with withHooks

Use withHooks() for initialization and cleanup logic.

Available Hooks
  • onInit() - Called when store is instantiated
  • onDestroy() - Called when store is destroyed
Common Use Cases
  • Loading initial data on store creation
  • Setting up subscriptions
  • Cleanup of resources
  • DevTools initialization

For implementation examples, see examples/core.md.

---

Pattern 7: Call State Pattern

Track loading, loaded, and error states for async operations using withCallState() from ngrx-toolkit or custom implementation.

States
  • loading - Operation in progress
  • loaded - Operation completed successfully
  • error - Operation failed with error
Benefits
  • Consistent loading UI patterns
  • Error handling standardization
  • Multiple collection support
  • DevTools visibility

For implementation examples, see examples/features.md.

---

Pattern 8: Context-Aware Features with withFeature (v20+)

Use withFeature() to create features that have access to the current store's methods and properties. Unlike signalStoreFeature(), withFeature receives the store instance, enabling reusable features that can call store-specific methods.

When to Use
  • Features that need to call existing store methods
  • Generic patterns like entity loaders that need store-specific fetch logic
  • When signalStoreFeature() is insufficient because the feature needs store context
import { withFeature } from "@ngrx/signals";

// withFeature receives the store instance
export const ProductStore = signalStore(
  { providedIn: "root" },
  withMethods((store) => ({
    load: rxMethod<string>(/* fetch logic */),
  })),
  withFeature((store) =>
    withEntityLoader((id) => firstValueFrom(store.load(id))),
  ),
);

---

Pattern 9: Derived Reactive State with withLinkedState (v20+)

Use withLinkedState() to create state signals that are automatically recomputed when their source signals change. Unlike withComputed(), linked state is writable and can be overridden.

When to Use
  • Derived state that also needs to be independently settable
  • Maintaining selection state across data changes
  • State that has a default derived value but can be overridden by user action
import { withLinkedState } from "@ngrx/signals";

export const FilterStore = signalStore(
  withState({ items: [] as Item[] }),
  withLinkedState(({ items }) => ({
    // Recomputes when items change, but can also be set independently
    selectedId: () => items()[0]?.id ?? null,
  })),
);

</patterns>

---

<integration>

Integration Guide

Angular DI Integration:

SignalStore is an Angular service. Use { providedIn: 'root' } for singletons or component-level providers: [Store] for scoped instances that are destroyed with the component.

See examples/core.md Pattern 5 and Component-Level Stores for DI patterns.

DevTools Integration:

Use withDevtools('name') from @angular-architects/ngrx-toolkit for Redux DevTools support. Place it before withState in the feature chain.

See examples/features.md Pattern 5 for setup.

Testing Integration:

  • Use unprotected() from @ngrx/signals/testing to bypass state protection for test setup
  • Use TestBed.configureTestingModule() with mocked services
  • Test store methods directly or through component integration

See examples/testing.md for patterns.

</integration>

---

<red_flags>

RED FLAGS

High Priority Issues:

  • Mutating state directly instead of using patchState - breaks reactivity and signal notifications
  • Using arrays instead of withEntities for entity collections - loses normalized state, O(1) lookups
  • Not wrapping async RxJS flows in rxMethod - loses cancellation and proper cleanup
  • Accessing store state outside computed/template - may not trigger updates

Medium Priority Issues:

  • Missing error handling in rxMethod pipelines
  • Deeply nested state (flatten with multiple state slices)
  • Not using entityConfig() for custom entity IDs (v18+)
  • Using signalMethod for async operations with race conditions (use rxMethod with switchMap)

Gotchas & Edge Cases:

  • withHooks.onInit() runs during store instantiation - be careful with side effects in tests
  • patchState with entity updaters requires collection option when using named collections
  • rxMethod auto-unsubscribes on destroy - do not manually unsubscribe
  • Protected state (v18+) prevents external patchState calls by default
  • Feature execution order matters - later features can access earlier ones
  • v19+ Deep Freeze: State values are recursively frozen with Object.freeze in dev mode - use withProps() for mutable objects like FormGroup

See reference.md for detailed anti-patterns with code examples.

</red_flags>

---

<critical_reminders>

CRITICAL REMINDERS

(You MUST use `patchState()` for ALL state updates - NEVER mutate state directly)

(You MUST wrap async operations in `rxMethod()` from `@ngrx/signals/rxjs-interop` for RxJS integration)

(You MUST use `withEntities()` from `@ngrx/signals/entities` for entity collections - NOT arrays in state)

(You MUST use named exports ONLY - NO default exports in any store files)

(You MUST use named constants for ALL numbers - NO magic numbers in state code)

Failure to follow these rules will cause reactivity bugs, state corruption, and inconsistent behavior.

</critical_reminders>

Related skills

FAQ

How do I update SignalStore state?

Use patchState() for all state updates and never mutate state directly; it ensures immutable updates without an Immer dependency.

How do async operations integrate with SignalStore?

Wrap async operations in rxMethod() from @ngrx/signals/rxjs-interop to bridge Angular Signals with RxJS.

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.