From harness-claude
Creates lightweight global stores with Zustand's create function for minimal-boilerplate React state management. Use for auth, UI flags, preferences without prop drilling or Redux complexity.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Create lightweight global stores with Zustand's create function for minimal-boilerplate state management
Creates and manages Zustand stores for React state management, covering creation, selectors, actions, updates, and performance best practices like shallow equality.
Guides Zustand state management in React and vanilla JS: stores, selectors, persistence, devtools, performance patterns, and non-React access.
Creates Zustand stores using the store creator pattern. Provides step-by-step guidance, production-ready code, and best practices for frontend state management tasks.
Share bugs, ideas, or general feedback.
Create lightweight global stores with Zustand's create function for minimal-boilerplate state management
create from Zustand. Define state and actions together in one function.set to update state.set shallow-merges by default. For nested updates, use the spread operator or the Immer middleware.useStore.getState() and useStore.setState().// stores/auth-store.ts
import { create } from 'zustand';
interface User {
id: string;
name: string;
email: string;
}
interface AuthStore {
user: User | null;
isAuthenticated: boolean;
login: (user: User) => void;
logout: () => void;
updateProfile: (updates: Partial<User>) => void;
}
export const useAuthStore = create<AuthStore>((set) => ({
user: null,
isAuthenticated: false,
login: (user) => set({ user, isAuthenticated: true }),
logout: () => set({ user: null, isAuthenticated: false }),
updateProfile: (updates) =>
set((state) => ({
user: state.user ? { ...state.user, ...updates } : null,
})),
}));
// Component usage — select only what you need
function UserGreeting() {
const userName = useAuthStore((state) => state.user?.name);
return <h1>Hello, {userName ?? 'Guest'}</h1>;
}
function LoginButton() {
const { login, isAuthenticated } = useAuthStore();
// Warning: destructuring without selector subscribes to ALL changes
// Prefer: const login = useAuthStore((state) => state.login);
}
// Outside React
async function fetchAndSetUser() {
const user = await fetch('/api/me').then((r) => r.json());
useAuthStore.getState().login(user);
}
Why selectors matter: Calling useAuthStore() with no argument subscribes to the entire store — the component re-renders on ANY state change. Always pass a selector to subscribe to only what the component needs.
set function behavior: set shallow-merges the object you pass with the current state. It does NOT deep merge. For nested state:
// Wrong — this replaces settings entirely
set({ settings: { theme: 'dark' } });
// Right — spread to preserve other fields
set((state) => ({ settings: { ...state.settings, theme: 'dark' } }));
get for reading current state in actions:
const useStore = create<Store>((set, get) => ({
items: [],
addItem: (item) => {
const current = get().items;
if (current.length >= 100) return; // Read before write
set({ items: [...current, item] });
},
}));
Store outside React: Zustand stores are vanilla JavaScript. useStore.getState() reads without subscribing. useStore.subscribe(listener) sets up manual subscriptions. This makes Zustand usable in Node.js, tests, and non-React code.
TypeScript pattern: Always define the store interface explicitly. Relying on inference from the create callback produces less readable types and breaks IDE autocompletion for complex stores.
https://zustand.docs.pmnd.rs/getting-started/introduction