Interactive Demo

Experience Zenty in Action

A powerful Zustand-based library that simplifies CRUD operations with elegant state management. Try the interactive demo below to see how easy it is to manage your data.

Products Example Store
Manage a collection of products with full CRUD operations. Notice how Zenty handles validation and errors automatically!
Not LoadedReady
Live Zustand Store State
{
  "entities": [],
  "loaded": false,
  "loading": false,
  "error": null
}
Current Products (0)

No products yet. Add some products to see them here!

Get Started with Zenty
Install and start using Zenty in your project

npm

1npm install zenty zustand

yarn

1yarn add zenty zustand

pnpm

1pnpm add zenty zustand
See how Zenty simplifies your code:
❌ Before: Traditional Zustand (Entities)

type Product {
  id: string
  name: string
  price?: number
  category?: string
}

type ProductState {
  entities: Product[]
  loaded: boolean
  loading: boolean
  error: string | null
  selectedId: number | null
}

type ProductActions {
  add: (item: Product) => void
  addMany: (items: Product[]) => void
  update: (id: number, patch: Partial<Product>) => void
  updateMany: (updates: Array<{ id: number; patch: Partial<Product> }>) => void
  delete: (id: number) => void
  deleteMany: (ids: number[]) => void
  find: (id: number) => Product | undefined
  has: (id: number) => boolean
  clear: () => void
  replaceAll: (items: Product[]) => void
  setLoading: (loading: boolean) => void
  setError: (error: string | null) => void
  setSelected: (id: number | null) => void
  getSelected: () => Product | undefined
}

// store.ts
import { create } from 'zustand'
import { subscribeWithSelector } from 'zustand/middleware'

const useProductStore = create<ProductState & ProductActions>()(
  subscribeWithSelector((set, get) => ({
    // State
    entities: [],
    loaded: false,
    loading: false,
    error: null,
    selectedId: null,

    // Actions
    add: (item) => set((state) => ({
      entities: [...state.entities, item],
      loaded: true,
      error: null
    })),

    addMany: (items) => set((state) => ({
      entities: [...state.entities, ...items],
      loaded: true,
      error: null
    })),

    update: (id, patch) => set((state) => {
      const index = state.entities.findIndex(item => item.id === id)
      if (index === -1) {
        return { error: `Product with id ${id} not found` }
      }
      const newEntities = [...state.entities]
      newEntities[index] = { ...newEntities[index], ...patch }
      return { entities: newEntities, error: null }
    }),

    updateMany: (updates) => set((state) => {
      const newEntities = [...state.entities]
      let hasError = false
      
      updates.forEach(({ id, patch }) => {
        const index = newEntities.findIndex(item => item.id === id)
        if (index !== -1) {
          newEntities[index] = { ...newEntities[index], ...patch }
        } else {
          hasError = true
        }
      })
      
      return {
        entities: newEntities,
        error: hasError ? 'Some updates failed' : null
      }
    }),

    delete: (id) => set((state) => {
      const exists = state.entities.some(item => item.id === id)
      if (!exists) {
        return { error: `Product with id ${id} not found` }
      }
      return {
        entities: state.entities.filter(item => item.id !== id),
        selectedId: state.selectedId === id ? null : state.selectedId,
        error: null
      }
    }),

    deleteMany: (ids) => set((state) => ({
      entities: state.entities.filter(item => !ids.includes(item.id)),
      selectedId: ids.includes(state.selectedId!) ? null : state.selectedId,
      error: null
    })),

    find: (id) => get().entities.find(item => item.id === id),

    has: (id) => get().entities.some(item => item.id === id),

    clear: () => set({
      entities: [],
      loaded: false,
      error: null,
      selectedId: null
    }),

    replaceAll: (items) => set({
      entities: items,
      loaded: true,
      error: null,
      selectedId: null
    }),

    setLoading: (loading) => set({ loading }),

    setError: (error) => set({ error }),

    setSelected: (id) => set({ selectedId: id }),

    getSelected: () => {
      const state = get()
      return state.selectedId ? state.find(state.selectedId) : undefined
    }
  }))
)

// Custom hooks for selectors
export const useProducts = () => useProductStore(state => state.entities)
export const useProductsLoaded = () => useProductStore(state => state.loaded)
export const useProductsLoading = () => useProductStore(state => state.loading)
export const useProductsError = () => useProductStore(state => state.error)
export const useSelectedProduct = () => useProductStore(state => state.getSelected())

// 150+ lines of boilerplate code just for basic CRUD!
✅ After: Zenty Entities Mode

import { createEntitiesStore } from 'zenty'

type Product {
  id: string
  name: string
  price?: number
  category?: string
}

const useProductStore = createEntitiesStore<Product>();
🚀 Result: 90% Less Code, 100% More Features
  • No boilerplate - Just one line to create a full-featured store
  • Built-in validation - Error handling and loading states included
  • TypeScript first - Full type safety out of the box
  • Powerful operations - CRUD, search, bulk operations, and more
  • Developer friendly - Intuitive API that just works