Zustand Store Boilerplate

A small, typed pattern for a Zustand store with a clear split between state and actions.

  • Typed state + actions: Safer refactors and autocomplete
  • Devtools ready: Named instance for easy inspection
  • Selector friendly: Tiny slices keep renders fast
  • Extensible: Add slices, persist, or immer gradually
import { createStore, type StateCreator, useStore } from "zustand";
import { devtools } from "zustand/middleware";

export type MyStoreType = {
  count: number;
  actions: {
    add(value: number): void;
    subtract(value: number): void;
  };
};

const creator: StateCreator<MyStoreType> = (set, get) => ({
  count: 1,
  actions: {
    add(value) {
      set({ count: get().count + value });
    },
    subtract(value) {
      set({ count: get().count - value });
    },
  },
});

export const MyStore = createStore(devtools(creator, { name: "MY_STORE" }));

export const useMyStore = <T>(selector: (s: MyStoreType) => T) =>
  useStore(MyStore, selector);

Example Usage

React hooks

import { useMyStore } from "./path/to/store";

export function Counter() {
  const count = useMyStore((s) => s.count);
  const { add, subtract } = useMyStore((s) => s.actions);
  return; /** jsx */
}

Outsite React (clientLoader)

Use the vanilla store directly in a React Router v7 clientLoader to initialize data before components render.

export async function clientLoader() {
  MyStore.setState({ count: 5 });
  return null;
}