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-storage

v1.0.1

Published

A universal solution combining @aivron/sync-storage and zust-api for React (web & desktop). It merges local persistence with a Zustand-inspired API to provide core storage operations, bulk actions, JSON support, TTL, and integrated React hooks.

Readme

zustand-storage

npm version Build Status License

zustand-storage is a powerful state management library built on top of @aivron/sync-storage and zust-api providing seamless persistence and optional direct database integration. It supports persistence via various storage engines (including localStorage and an in-memory fallback) and integrates with databases such as MongoDB and PostgreSQL. With its flexible API, you can easily create a synced store that handles client-side state, auto-persistence, and even database fetching/updating—all following DRY and SOLID principles.

Table of Contents

Features

  • Synced State Management: Create a state store with built-in persistence using a configurable storage engine.

  • Auto-Persistence: Automatically persist state changes to a storage engine (e.g., localStorage) with options for debouncing and interval-based persistence.

  • Flexible Merge Strategies: Merge persisted state with your initial state using deep, shallow, or replace strategies.

  • Middleware Support: Easily add middleware to transform or log state updates.

  • Database Integration: Directly fetch from or save to databases (MongoDB, PostgreSQL) with built-in methods. Integrate your own database client by implementing the common DatabaseClient interface.

Installation

Install the package via npm:

npm install zustand-storage

Or using yarn:

yarn add zustand-storage

Getting Started

Creating a Basic Synced Store

Below is an example of creating a simple synced store with default persistence using localStorage or an in-memory fallback.

import { createSyncedStore } from 'zustand-storage';

interface AppState {
  count: number;
}

const store = createSyncedStore<AppState, { increment: () => void }>({
  key: 'app-state', // unique key for persistence
  initialState: { count: 0 },
  actions: (set, get) => ({
    increment: () => set({ count: get().count + 1 }),
  }),
  syncOptions: {
    debounce: 300,         // Debounce persistence by 300ms
    persistOn: 'change',   // Persist state on every change
    onError: (error) => console.error("Sync error:", error),
  },
  mergeStrategy: 'deep',   // Use deep merging to combine states
});

React Integration

Use the provided React hook to access the store in your components:

import React from 'react';
import { useStore } from 'zustand-storage';

function Counter() {
  const [state, actions] = useStore();
  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={actions.increment}>Increment</button>
    </div>
  );
}

export default Counter;

API Reference

Store Configuration & Options

When creating a store, the configuration object (of type SyncedStoreConfig) supports:

  • key (string): Unique identifier for persisting state.

  • initialState (object): Your store’s initial state.

  • actions (function): A function that receives the state setter (StoreSet) and a getter to build your actions.

  • syncOptions (object): Configure persistence with:

    • debounce (number): Delay for debouncing persistence.
    • serialize / deserialize: Custom serialization functions (defaults to JSON.stringify/JSON.parse).
    • persistOn: Either 'change' (persist immediately on state change) or 'interval' (persist periodically).
    • interval (number): Interval in milliseconds when using interval mode.
    • onError: Callback for error handling during persistence.
  • mergeStrategy (string): Strategy for merging persisted state: 'deep', 'shallow', or 'replace'.

  • migrate (function): A migration function to update persisted state if needed.

Database Integration

You can extend the store to directly interact with a database by adding the optional database configuration.

interface DatabaseIntegration<State> {
  client: any;  // Instance of a database client (e.g., MongoDBClient or PostgreSQLClient)
  collectionOrTable: string; // Name of the collection (for MongoDB) or table (for PostgreSQL)
  fromRecord?: (record: any) => Partial<State>; // Transform DB record to state
  toRecord?: (state: State) => object;          // Transform state to DB record
  onError?: (error: Error) => void;             // Database operation error handler
}

When provided, two new methods become available on the store:

  • fetchFromDB(query: object): Promise<void> Fetches data from the database based on the query and merges it into the store.

  • saveToDB(query: object): Promise<void> Saves the current store state to the database. If the record exists (based on the query), it will update; otherwise, it will insert a new record.

Database Clients

zustand-storage provides two built-in database client implementations. These classes implement a common DatabaseClient interface, so you can easily swap or extend them.

MongoDB Client

The MongoDBClient class uses the official MongoDB Node.js driver.

Usage Example:

import { MongoDBClient } from 'zustand-storage/database/MongoDBClient';

const mongoClient = new MongoDBClient('mongodb://localhost:27017', 'mydb');
await mongoClient.connect();

const users = await mongoClient.find('users', { active: true });
console.log(users);

await mongoClient.disconnect();

PostgreSQL Client

The PostgreSQLClient class uses the pg library to interact with PostgreSQL.

Usage Example:

import { PostgreSQLClient } from 'zustand-storage/database/PostgreSQLClient';

const pgClient = new PostgreSQLClient('postgresql://user:pass@localhost:5432/mydb');
await pgClient.connect();

const users = await pgClient.find('users', { active: true });
console.log(users);

await pgClient.disconnect();

Advanced Usage

Integrating Database with the Store

You can create a store that not only persists state locally but also integrates with a database. This is especially useful for syncing remote data with client state.

Example:

import { createSyncedStore } from 'zustand-storage';
import { MongoDBClient } from 'zustand-storage/database/MongoDBClient';

interface AppState {
  users: any[];
}

const mongoClient = new MongoDBClient('mongodb://localhost:27017', 'mydb');

const store = createSyncedStore<AppState, { addUser: (user: any) => void }>({
  key: 'app-state',
  initialState: { users: [] },
  actions: (set, get) => ({
    addUser: (user) => set({ users: [...get().users, user] }),
  }),
  syncOptions: {
    debounce: 300,
    persistOn: 'change',
    onError: (error) => console.error("Sync error:", error),
  },
  mergeStrategy: 'deep',
  database: {
    client: mongoClient,
    collectionOrTable: 'users',
    fromRecord: (record) => ({ users: [record] }), // Custom transform function
    toRecord: (state) => state.users[0],              // Custom transform function
    onError: (error) => console.error("DB Integration Error:", error),
  },
});

// Fetch data from the database and merge it into the store.
store.fetchFromDB && store.fetchFromDB({ active: true });

// Save the current state to the database.
store.saveToDB && store.saveToDB({ id: 123 });

Customizing the Storage Engine

If you need a custom persistence mechanism, you can pass your own storage engine that implements the StorageEngine interface.

import { createSyncedStore, StorageEngine } from 'zustand-storage';

const customStorageEngine: StorageEngine = {
  getItem: (key) => {
    // Your custom logic to get an item.
    return localStorage.getItem(key);
  },
  setItem: (key, value) => {
    // Your custom logic to set an item.
    localStorage.setItem(key, value);
  },
  removeItem: (key) => {
    // Your custom logic to remove an item.
    localStorage.removeItem(key);
  }
};

const store = createSyncedStore({
  key: 'app-state',
  initialState: { count: 0 },
  actions: (set, get) => ({
    increment: () => set({ count: get().count + 1 })
  }),
  storageEngine: customStorageEngine,
});

Error Handling

Both local persistence and database operations use robust error handling:

  • Persistence Errors: Use the onError callback in syncOptions to catch and handle any persistence errors.
  • Database Errors: Each database operation is wrapped in try/catch blocks. You can provide a custom error handler in the database configuration (database.onError).

Contributing

Contributions are welcome! Please check the issues for bugs or feature requests and feel free to submit pull requests.

License

This project is licensed under the MIT License. See the LICENSE file for details.

Repository

The source code is available on GitHub: github.com/aivron/zustand-storage


Happy coding!