Managing shared state between components gets more difficult as your Angular application expands.  NGXS (pronounced "nexus") offers a simple, intuitive, and powerful state management pattern for Angular apps, based on the principles of Redux but with a more Angular-friendly approach. This post will discuss how to effectively manage application state by configuring and utilizing NGXS.

Why NGXS?
NGXS provides:

  • A simple API using decorators and observables.
  • Type-safe state and actions.
  • Built-in support for plugins (logger, devtools, storage).
  • Easy-to-understand state mutation via actions.

Step 1. Install NGXS
Use the Angular CLI to install the NGXS packages:
npm install @ngxs/store --save


Optional DevTools and plugins:
npm install @ngxs/devtools-plugin @ngxs/logger-plugin --save

Step 2. Create a State Model
Create an interface to define your state:
// models/todo.model.ts
export interface Todo {
  id: number;
  title: string;
  completed: boolean;
}

Step 3. Define Actions
Actions are plain classes that represent events in your app.

// actions/todo.actions.ts
export class AddTodo {
  static readonly type = '[TODO] Add';
  constructor(public payload: string) {}
}

export class ToggleTodo {
  static readonly type = '[TODO] Toggle';
  constructor(public id: number) {}
}

Step 4. Create the State
Define the NGXS state using the @State() decorator.
// state/todo.state.ts
import { State, Action, StateContext, Selector } from '@ngxs/store';
import { AddTodo, ToggleTodo } from '../actions/todo.actions';
import { Todo } from '../models/todo.model';

@State<Todo[]>({
  name: 'todos',
  defaults: []
})
export class TodoState {

  @Selector()
  static getTodos(state: Todo[]) {
    return state;
  }

  @Action(AddTodo)
  add({ getState, patchState }: StateContext<Todo[]>, { payload }: AddTodo) {
    const state = getState();
    const newTodo: Todo = { id: Date.now(), title: payload, completed: false };
    patchState([...state, newTodo]);
  }

  @Action(ToggleTodo)
  toggle({ getState, patchState }: StateContext<Todo[]>, { id }: ToggleTodo) {
    const state = getState();
    const updated = state.map(todo =>
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    );
    patchState(updated);
  }
}


Step 5. Register the State in the App Module

// app.module.ts
import { NgxsModule } from '@ngxs/store';
import { TodoState } from './state/todo.state';

@NgModule({
  declarations: [...],
  imports: [
    NgxsModule.forRoot([TodoState]),
  ],
  bootstrap: [...]
})
export class AppModule {}

Step 6. Use the State in Components
// app.component.ts
import { Store, Select } from '@ngxs/store';
import { Observable } from 'rxjs';
import { TodoState } from './state/todo.state';
import { AddTodo, ToggleTodo } from './actions/todo.actions';

@Component({...})
export class AppComponent {
  @Select(TodoState.getTodos) todos$: Observable<Todo[]>;

  constructor(private store: Store) {}

  addTodo(title: string) {
    this.store.dispatch(new AddTodo(title));
  }

  toggleTodo(id: number) {
    this.store.dispatch(new ToggleTodo(id));
  }
}

Conclusion
NGXS brings a clean and declarative way to manage state in Angular apps. With actions, selectors, and plugins, it streamlines complex state flows into something more maintainable and scalable. Happy coding !