MCPcopy Index your code
hub / github.com/JoaoPauloLousada/ngx-zustand

github.com/JoaoPauloLousada/ngx-zustand @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
57 symbols 97 edges 32 files 0 documented · 0%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

NgxZustand

The Zustand adapter for angular.

Installation

with npm:

npm install ngx-zustand zustand

with yarn:

yarn add ngx-zustand zustand

First create a store

Create a service that extends ZustandBaseService.

interface CounterState {
  counter: number;
  increment: () => void;
  decrement: () => void;
}

@Injectable({
  providedIn: 'root',
})
export class CounterService extends ZustandBaseService<CounterState> {
  initStore() {
    return (set) => ({
      counter: 0,
      increment: () => set((state) => ({ counter: state.counter + 1 })),
      decrement: () => set((state) => ({ counter: state.counter - 1 })),
    });
  }
}

Use the service in your components

@Component({
  selector: 'app-counter-page',
  standalone: true,
  imports: [CommonModule],
  template: `






        count: {{ store.counter }}





<button (click)="store.increment()">+</button>




<button (click)="store.decrement()">-</button>











  `,
  styles: [],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CounterPageComponent {
  private counterService = inject(CounterService);
  store$ = this.counterService.useStore();
}

Recipes

Fetching Everything

store$ = this.store.useStore();

Selecting multiple state slices

foo$ = this.store.useStore((state) => state.foo);
bar$ = this.store.useStore((state) => state.bar);
fooAndBar$ = this.store.useStore((state) => ({
  foo: state.foo,
  bar: state.bar,
}));

Async actions

Just call set when you're ready, zustand doesn't care if your actions are async or not.

export class TodosStore extends ZustandBaseService<TodosState> {
  initStore() {
    return (set) => ({
      todos: [],
      loadTodos: () => {
        this.http.get<Todo[]>().subscribe((todos) => set({ todos }));
      },
    });
  }
}

Read from state in actions

set allows fn-updates set(state => result), but you still have access to state outside of it through get.

export class TodosStore extends ZustandBaseService<TodosState> {
  initStore() {
    return (set, get) => ({
      todos: [],
      action: () => {
        const todos = get().todos;
      },
    });
  }
}

Redux devtools middleware

You can override createStore function in order to include the middlewares you need.

import { devtools } from 'zustand/middleware';

@Injectable({
  providedIn: 'root',
})
export class CounterService extends ZustandBaseService<CounterState> {
  initStore() {
    return devtools<CounterState>((set) => ({
      counter: 0,
      increment: () => set((state) => ({ counter: state.counter + 1 })),
      decrement: () => set((state) => ({ counter: state.counter - 1 })),
    }));
  }
}

Persist middleware

import { createJSONStorage, persist } from 'zustand/middleware';

@Injectable({
  providedIn: 'root',
})
export class CounterService extends ZustandBaseService<CounterState> {
  initStore(): StateCreator<CounterState> {
    return (set) => ({
      counter: 0,
      increment: () => set((state) => ({ counter: state.counter + 1 })),
      decrement: () => set((state) => ({ counter: state.counter - 1 })),
    });
  }

  override createStore() {
    return createStore(
      persist<CounterState>(this.initStore(), {
        name: 'counterStore',
        storage: createJSONStorage(() => sessionStorage),
      })
    );
  }
}

Middleware

You can functionally compose your store any way you like. Please check typescript guide to a better explanation of how to type middlewares.

// Log every time state is changed
const logMiddleware = (config) => (set, get, api) =>
  config(
    (...args) => {
      console.log('  applying', args);
      set(...args);
      console.log('  new state', get());
    },
    get,
    api
  );

export class CounterService extends ZustandBaseService<CounterState> {
  initStore() {
    return logMiddleware((set) => ({
      counter: 0,
      increment: () => set((state) => ({ counter: state.counter + 1 })),
      decrement: () => set((state) => ({ counter: state.counter - 1 })),
    }));
  }
}

Extension points exported contracts — how you extend this code

Core symbols most depended-on inside this repo

Shape

Class 34
Method 9
Function 8
Interface 6

Languages

TypeScript100%

Modules by API surface

packages/docs-site/src/app/layout/nav-list.component.ts6 symbols
packages/playground/src/app/todo-page/todo-store.service.ts5 symbols
packages/playground/src/app/counter.service.ts5 symbols
packages/core/src/lib/zustand-base.service.ts5 symbols
packages/core/src/lib/zustand-base.service.spec.ts4 symbols
packages/core/src/lib/use-store.spec.ts4 symbols
packages/playground/src/app/example.service.ts3 symbols
packages/docs-site/src/app/layout/toolbar.component.ts3 symbols
packages/playground/src/app/todo-page/todo.module.ts2 symbols
packages/playground/src/app/todo-page/todo.http.service.ts2 symbols
packages/playground/src/app/todo-page/todo-page.component.ts2 symbols
packages/playground/src/app/http.service.ts2 symbols

For agents

$ claude mcp add ngx-zustand \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page