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
error
error
error
error
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 zustandyarn
1yarn add zenty zustandpnpm
1pnpm add zenty zustandSee 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