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

zustand-dot

v1.1.0

Published

Zustand middleware for deep dot-path access and reactive hooks

Readme

Zustand Dot Path Middleware

A typesafe, reactive middleware for Zustand that enables deep property access using dot notation. Support your complex state objects with granular subscriptions, immutable updates, and stable default values.

Features

  • 🎯 Deep Access: Get, set, and subscribe to deeply nested values using dot path strings (e.g., user.posts.0.title).
  • 🛡️ Fully Typesafe: Paths and return values are strictly inferred. Invalid paths throw compile-time errors.
  • Reactive Hook: usePath subscribes only to the specific path you request.
  • 🧘 Stable Defaults: usePath memoizes default values deeply, preventing unnecessary re-renders when passing objects/arrays as defaults.
  • 🔄 Immutable Updates: setPath performs structural sharing, updating only what changed.
  • 🔢 Array Support: seamless array access via dot notation (items.0) or brackets (items[0]).

Installation

npm install zustand-dot zustand
# or
yarn add zustand-dot zustand
# or
pnpm add zustand-dot zustand
# or
bun add zustand-dot zustand

Setup

Wrap your store creator with dotPath.

import { create } from 'zustand'
import { dotPath } from 'zustand-dot'

interface State {
  user: {
    profile: {
      name: string
      age: number
    }
    posts: Array<{ id: number; title: string }>
  }
}

const useStore = create<State>()(
  dotPath((set) => ({
    user: {
      profile: { name: 'Alice', age: 30 },
      posts: [],
    },
  }))
)

API

usePath(path, defaultValue?)

A React hook that subscribes to a specific path in the store.

// Component re-renders ONLY when user.profile.name changes
const [name, setName] = useStore.usePath('user.profile.name')

// With a default value (memoized deeply!)
const [posts, setPosts] = useStore.usePath('user.posts', [])
// ^ Safe! Passing [] as default won't cause infinite re-renders.
  • Arguments:
    • path: string - Dot notation path to the value.
    • defaultValue: (optional) - Value to return if the path resolves to null or undefined.
  • Returns: [value, setter] tuple.

setPath(path, valueOrUpdater)

Updates a value deeply, creating nested objects/arrays if they don't exist.

// Set a value directly
useStore.setPath('user.profile.age', 31)

// Functional update (receives current raw value at path)
useStore.setPath('user.posts', (posts) => [...posts, { id: 1, title: 'New' }])

// Arrays via index
useStore.setPath('user.posts.0.title', 'Updated Title')
  • Arguments:
    • path: string.
    • valueOrUpdater: The new value, or a function (prev) => next.

getPath(path, defaultValue?)

Imperatively get a value from the store (non-reactive).

const age = useStore.getPath('user.profile.age')

resetPath(path)

Resets a specific path (subtree) back to its initial state captured at store creation time.

// Reverts user.profile to { name: 'Alice', age: 30 }
useStore.resetPath('user.profile')

TypeScript Support

The middleware leverages advanced recursive types to provide autocomplete and validation.

// ✅ Valid
useStore.setPath('user.profile.name', 'Bob')

// ❌ Error: Property 'nominative' does not exist
useStore.setPath('user.profile.nominative', 'Bob')

// ❌ Error: Type 'number' is not assignable to type 'string'
useStore.setPath('user.profile.name', 123)

Path Syntax

  • Dot notation: a.b.c
  • Array indices: items.0.id
  • Brackets: items[0].id
  • Quoted keys: config["remote.url"]

Performance

All dot-path operations use cached path parsing, iterative deep-set, and closure-free state updates. Benchmarked against equivalent vanilla Zustand patterns using vitest bench.

| Operation | vs Vanilla | Absolute (per op) | | -------------------------- | ---------------- | ----------------- | | Read — shallow | 1.09x slower | ~22 ns | | Read — deep (4 levels) | 1.75x slower | ~36 ns | | Read — array element | 1.60x slower | ~33 ns | | Write — shallow | 1.10x faster | ~36 ns | | Write — deep (4 levels) | 2.52x slower | ~162 ns | | Write — functional updater | 1.40x slower | ~61 ns | | Write — array element | 2.20x slower | ~144 ns | | Reset — subtree | 1.09x slower | ~953 ns |

Shallow reads are within 10% of direct property access. Shallow writes are faster than vanilla setState because setPath uses replace: true, skipping Zustand's partial state merge.

Run npm run bench to reproduce.

License

MIT