npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sinakhx/use-zustand-store

v0.3.0

Published

helpers for using zustand as a local store in react apps

Readme

@sinakhx/useZustandStore

npm license types

custom helpers for using zustand in react apps. it can be used for creating local (component scoped) stores using Zustand. So that:

  • you won't need to worry about garbage collecting your store on page components' unmount lifecycle.
  • you can get rid of using multiple selectors to acces different parts of the store (as it's using react-tracked under the hood)
  • you avoid making your codebase weird with currying, Providers, mind-boggling type annotations, etc.

Installation

npm install @sinakhx/use-zustand-store

Usage

Creating a store is exactly the same way as creating a store in Zustand. You only need to change Zustand's create function with this library's createZustandStore function. Everything else is the same. (It's just a wrapper to avoid nesting due to currying)

Example counter app:

counterStore.ts

import { createZustandStore, mutateStoreItem } from '@sinakhx/use-zustand-store'

interface ICounterStore {
    count: number
    increment: () => void
}

export const counterStore = createZustandStore<ICounterStore>((set) => ({
    count: 0,
    increment: () => set((state) => ({ count: state.count + 1 })),
}))

CounterComponent.tsx

import { useZustandStore } from '@sinakhx/use-zustand-store'
import { counterStore } from './counterStore'

const CounterComponent = () => {
    const store = useZustandStore(counterStore)
    return <button onClick={store.increment}>{store.count}</button>
}

export default CounterComponent

Now the store is bound to the component. By changing the page route (unmounting the component), the store gets garbage collected & by going back to the page (mounting the component again), a fresh store is created.

That's done! Happy coding!

Instead of using Immer or nested destructuring to mutate the store, you can use the mutateStoreItem helper.

The following example demonstrates how to reduce multiple useState hooks to a single store.

tableStore.ts

import { createZustandStore, mutateStoreItem } from '@sinakhx/use-zustand-store'

type TableRow = {
    id: number
    name: string
    age: number
}

interface ITableStore {
    rows: Array<TableRow>
    setRows: (rows: TableRow[]) => void
    selectedRow: TableRow | null
    setSelectedRow: (row: TableRow | null) => void
    handleDeleteRow: (id: number) => void
}

const counterStore = createZustandStore<ITableStore>((set, get) => ({
    rows: [],
    setRows: (rows) => set(mutateStoreItem({ rows })),
    selectedRow: null,
    setSelectedRow: (row) => set(mutateStoreItem({ selectedRow: row })),
    handleDeleteRow: (id) => {
        const newRows = get().rows.filter((row) => row.id !== id)
        get().setRows(newRows)
    },
}))

mutateStoreItem is using optics-ts to access the store's state. As a result one can also easily mutate a nested store item by providing its path as object key. e.g: set(mutateStoreItem({ 'user.info.name': 'John' })).

counterStore.ts

import { createZustandStore } from '@sinakhx/use-zustand-store'

interface ICounterStore {
    count: number
    increment: () => void
}

interface ICounterProps {
    initialCount: number
}

export const counterStoreFactory = ({ initialCount } : ICounterProps) => createZustandStore<ICounterStore>((set) => ({
    count: initialCount,
    increment: () => set((state) => ({ count: state.count + 1 })),
}))

CounterComponent.tsx

import { useZustandStore } from '@sinakhx/use-zustand-store'
import { counterStoreFactory } from './counterStore'

interface ICounterProps {
    initialCount: number
}

const CounterComponent = ({ initialCount }: ICounterProps) => {
    const store = useZustandStore(counterStoreFactory({ initialCount }))
    return <button onClick={store.increment}>{store.count}</button>
}

export default CounterComponent

In that case, you can create a global version of the useZustandStore hook by using the createTrackedSelector helper from react-tracked

counterStore.ts

import { createZustandStore, createTrackedSelector } from '@sinakhx/use-zustand-store'

interface ICounterStore {
    count: number
    increment: () => void
}

const counterStore = createZustandStore<ICounterStore>((set) => ({
    count: 0,
    increment: () => set((state) => ({ count: state.count + 1 })),
}))

export const useGlobalCounterStore = createTrackedSelector(counterStore())

CounterComponent.tsx

// import { useZustandStore } from '@sinakhx/use-zustand-store'
import { useGlobalCounterStore } from './counterStore'

const CounterComponent = () => {
    const store = useGlobalCounterStore()
    return <button onClick={store.increment}>{store.count}</button>
}

export default CounterComponent

now the store is independent from the components & will keep its state regardless of the route changes.


Contributing

Please feel free to open an issue or create a pull request to add a new feature or fix a bug. (see contributing for more details)


License

The MIT License (MIT)

© 2022 Sina Khodabandehloo