NgRx Store, Effects, Entity, and ComponentStore for Angular state management. Covers reactive patterns with signals integration. USE WHEN: user mentions "NgRx", "Angular state management", "NgRx Store", "NgRx Effects", "NgRx Entity", "ComponentStore", "Angular Redux" DO NOT USE FOR: Redux Toolkit - use `redux-toolkit`, Zustand - use `zustand`, Pinia - use `pinia`, simple Angular signals state
Scanned 9/8/2026
Install to Claude Code
npx -y skills add claude-dev-suite/claude-dev-suite --skill ngrx --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Ngrx?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/claude-dev-suite-ngrx)More formats (shields.io, HTML) on the badges page.
---
name: ngrx
description: |
NgRx Store, Effects, Entity, and ComponentStore for Angular state management.
Covers reactive patterns with signals integration.
USE WHEN: user mentions "NgRx", "Angular state management", "NgRx Store", "NgRx Effects",
"NgRx Entity", "ComponentStore", "Angular Redux"
DO NOT USE FOR: Redux Toolkit - use `redux-toolkit`, Zustand - use `zustand`,
Pinia - use `pinia`, simple Angular signals state
allowed-tools: Read, Grep, Glob, Write, Edit
---
# NgRx State Management - Quick Reference
> **Deep Knowledge**: Use `mcp__documentation__fetch_docs` with technology: `angular` for NgRx patterns.
## Store Setup
```typescript
// app.config.ts
import { provideStore } from '@ngrx/store';
import { provideEffects } from '@ngrx/effects';
import { provideStoreDevtools } from '@ngrx/store-devtools';
export const appConfig: ApplicationConfig = {
providers: [
provideStore({ users: usersReducer }),
provideEffects([UsersEffects]),
provideStoreDevtools({ maxAge: 25, logOnly: !isDevMode() }),
]
};
```
## Actions
```typescript
import { createActionGroup, emptyProps, props } from '@ngrx/store';
export const UsersActions = createActionGroup({
source: 'Users',
events: {
'Load Users': emptyProps(),
'Load Users Success': props<{ users: User[] }>(),
'Load Users Failure': props<{ error: string }>(),
'Add User': props<{ user: User }>(),
'Remove User': props<{ id: number }>(),
},
});
```
## Reducer with createFeature
```typescript
import { createFeature, createReducer, on } from '@ngrx/store';
export interface UsersState {
users: User[];
loading: boolean;
error: string | null;
}
const initialState: UsersState = {
users: [],
loading: false,
error: null,
};
export const usersFeature = createFeature({
name: 'users',
reducer: createReducer(
initialState,
on(UsersActions.loadUsers, (state) => ({ ...state, loading: true, error: null })),
on(UsersActions.loadUsersSuccess, (state, { users }) => ({
...state, users, loading: false,
})),
on(UsersActions.loadUsersFailure, (state, { error }) => ({
...state, error, loading: false,
})),
on(UsersActions.addUser, (state, { user }) => ({
...state, users: [...state.users, user],
})),
on(UsersActions.removeUser, (state, { id }) => ({
...state, users: state.users.filter(u => u.id !== id),
})),
),
});
export const { selectUsers, selectLoading, selectError } = usersFeature;
```
## Effects
```typescript
import { inject } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, exhaustMap, map, of } from 'rxjs';
export class UsersEffects {
private actions$ = inject(Actions);
private userService = inject(UserService);
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(UsersActions.loadUsers),
exhaustMap(() =>
this.userService.getUsers().pipe(
map(users => UsersActions.loadUsersSuccess({ users })),
catchError(error => of(UsersActions.loadUsersFailure({ error: error.message }))),
)
)
)
);
}
```
## Component Integration with Signals
```typescript
import { Component, inject } from '@angular/core';
import { Store } from '@ngrx/store';
import { selectUsers, selectLoading } from './users.reducer';
import { UsersActions } from './users.actions';
@Component({
standalone: true,
template: `
@if (loading()) {
<app-spinner />
}
@for (user of users(); track user.id) {
<app-user-card [user]="user" (delete)="remove(user.id)" />
}
`
})
export class UsersComponent {
private store = inject(Store);
users = this.store.selectSignal(selectUsers);
loading = this.store.selectSignal(selectLoading);
constructor() {
this.store.dispatch(UsersActions.loadUsers());
}
remove(id: number) {
this.store.dispatch(UsersActions.removeUser({ id }));
}
}
```
## ComponentStore (Lightweight Alternative)
```typescript
import { Injectable } from '@angular/core';
import { ComponentStore } from '@ngrx/component-store';
import { tapResponse } from '@ngrx/operators';
import { switchMap } from 'rxjs';
interface UsersState {
users: User[];
loading: boolean;
}
@Injectable()
export class UsersStore extends ComponentStore<UsersState> {
constructor(private userService: UserService) {
super({ users: [], loading: false });
}
readonly users = this.selectSignal(state => state.users);
readonly loading = this.selectSignal(state => state.loading);
readonly loadUsers = this.effect<void>(trigger$ =>
trigger$.pipe(
switchMap(() => {
this.patchState({ loading: true });
return this.userService.getUsers().pipe(
tapResponse(
users => this.patchState({ users, loading: false }),
() => this.patchState({ loading: false }),
)
);
})
)
);
}
```
## Anti-Patterns
| Anti-Pattern | Why It's Bad | Correct Approach |
|--------------|--------------|------------------|
| Store for local component state | Over-engineering | Use signals or ComponentStore |
| Dispatching in effects | Infinite loops | Return actions from effects |
| Not using `createActionGroup` | Verbose action creators | Use `createActionGroup` |
| Subscribing to store in components | Memory leaks | Use `selectSignal()` |
## Quick Troubleshooting
| Issue | Likely Cause | Solution |
|-------|--------------|----------|
| State not updating | Immutability violation | Return new object in reducer |
| Effect not triggering | Wrong action type | Check `ofType()` matches |
| DevTools not showing | Missing provider | Add `provideStoreDevtools()` |
| Selector returning undefined | Feature not registered | Add to `provideStore()` |
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!