| name | zustand-store-development |
| description | Guide for creating and modifying Zustand stores with IndexedDB persistence in the DEVS platform. Use this when asked to create a new store, modify store logic, or work with state management. Use when this capability is needed. |
| metadata | {"author":"codename-co"} |
Zustand Store Development for DEVS
When creating or modifying Zustand stores in the DEVS platform, follow these patterns and best practices.
Directory Structure
Stores are located in src/stores/. Each store manages a specific domain entity.
Store Pattern Template
import { create } from 'zustand'
import { db } from '@/lib/db'
import { toast } from '@/lib/toast'
interface EntityState {
entities: Entity[]
isLoading: boolean
error: string | null
}
interface EntityActions {
loadEntities: () => Promise<void>
createEntity: (data: CreateEntityInput) => Promise<Entity>
updateEntity: (id: string, updates: Partial<Entity>) => Promise<void>
deleteEntity: (id: string) => Promise<void>
getEntityById: (id: string) => Entity | undefined
}
export const useEntityStore = create<EntityState & EntityActions>(
(set, get) => ({
entities: [],
isLoading: false,
error: null,
loadEntities: async () => {
set({ isLoading: true, error: null })
try {
const entities = await db.entities.toArray()
set({ entities, isLoading: false })
} catch (error) {
set({ error: (error as Error).message, isLoading: false })
toast.error('Failed to load entities')
}
},
createEntity: async (data) => {
const entity: Entity = {
id: crypto.randomUUID(),
...data,
createdAt: new Date(),
updatedAt: new Date(),
}
await db.entities.add(entity)
set((state) => ({ entities: [...state.entities, entity] }))
return entity
},
updateEntity: async (id, updates) => {
const updatedData = { ...updates, updatedAt: new Date() }
await db.entities.update(id, updatedData)
set((state) => ({
entities: state.entities.map((e) =>
e.id === id ? { ...e, ...updatedData } : e,
),
}))
},
deleteEntity: async (id) => {
await db.entities.delete(id)
set((state) => ({
entities: state.entities.filter((e) => e.id !== id),
}))
},
getEntityById: (id) => {
return get().entities.find((e) => e.id === id)
},
}),
)
export const getEntityById = (id: string) =>
useEntityStore.getState().getEntityById(id)
export const createEntity = (data: CreateEntityInput) =>
useEntityStore.getState().createEntity(data)
export const updateEntity = (id: string, updates: Partial<Entity>) =>
useEntityStore.getState().updateEntity(id, updates)
export const deleteEntity = (id: string) =>
useEntityStore.getState().deleteEntity(id)
Critical Rules
-
Export standalone functions: Always export functions directly for use in non-React contexts (lib/, orchestrator, etc.)
import { getAgentById, createAgent } from '@/stores/agentStore'
const { getAgentById } = useAgentStore()
-
Database initialization: Always check db.isInitialized() before operations if the store loads at app startup
-
Optimistic updates: Update local state immediately, then persist to IndexedDB
-
Error handling: Use try/catch with toast notifications for user feedback
-
Timestamps: Always update updatedAt when modifying entities
Testing Requirements (TDD Mandatory)
All store changes MUST follow TDD:
- Create test file at
src/test/stores/{storeName}.test.ts
- Write failing tests first
- Implement minimum code to pass
- Run
npm run test:coverage before committing
Example test structure:
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { useEntityStore } from '@/stores/entityStore'
describe('entityStore', () => {
beforeEach(() => {
useEntityStore.setState({ entities: [], isLoading: false, error: null })
})
describe('createEntity', () => {
it('should create entity with correct structure', async () => {
const result = await useEntityStore
.getState()
.createEntity({ name: 'Test' })
expect(result).toHaveProperty('id')
expect(result.name).toBe('Test')
})
})
})
Existing Stores Reference
agentStore - AI agent management with slug generation
taskStore - Task lifecycle and requirement validation
conversationStore - Multi-agent chat history
artifactStore - Task deliverables
contextStore - Inter-agent context sharing
userStore - User preferences and settings
agentMemoryStore - Agent learning and memory
Converted and distributed by TomeVault — claim your Tome and manage your conversions.