# @cubux/effector-persistent

> Persist data in effector store.

Latest version **0.9.0** (published 2026-08-14) · MIT license · 0 weekly downloads

## Install

```sh
npm install @cubux/effector-persistent
pnpm add @cubux/effector-persistent
yarn add @cubux/effector-persistent
bun add @cubux/effector-persistent
```

## Health

**Score 65/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; recently updated; high quality score.

Warnings: low downloads; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.9.0 |
| Published | 2026-08-14 |
| First published | 2022-09-06 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=24 |
| Dependencies | 0 |
| Unpacked size | 52.9 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 0 |
| Author | Vovan-VE |
| Maintainers | vovan-ve |
| Keywords | effector, store, persist, persistent, localStorage, sessionStorage, indexedDB |

## Links

- npm: https://www.npmjs.com/package/@cubux/effector-persistent
- Repository: https://github.com/cubux-net/effector-persistent
- Homepage: https://github.com/cubux-net/effector-persistent#readme
- Issues: https://github.com/cubux-net/effector-persistent/issues
- npm.io page: https://npm.io/package/@cubux/effector-persistent

## Alternatives

- [localforage](https://npm.io/package/localforage.md) — 6.2M weekly downloads
- [localforage-observable](https://npm.io/package/localforage-observable.md) — 30.8K weekly downloads
- [@y/y](https://npm.io/package/@y/y.md) — 30.1K weekly downloads
- [@metaobjectsdev/render](https://npm.io/package/@metaobjectsdev/render.md) — 3.5K weekly downloads
- [@ledgerhq/coin-algorand](https://npm.io/package/@ledgerhq/coin-algorand.md) — 1.1K weekly downloads

## Recent versions

- 0.9.0 (latest) — 2026-08-14
- 0.8.0 — 2024-07-22
- 0.7.0 — 2023-12-17
- 0.6.0 — 2023-12-11
- 0.5.0 — 2023-05-23
- 0.4.0 — 2022-12-13
- 0.3.0 — 2022-12-11
- 0.2.0 — 2022-09-09
- 0.1.0 — 2022-09-06

## README

# `@cubux/effector-persistent`

[![NPM latest](https://img.shields.io/npm/v/@cubux/effector-persistent.svg)](https://www.npmjs.com/package/@cubux/effector-persistent)

Persist data in effector store.

```ts
import { createStore } from "effector";
import { withPersistent } from "@cubux/effector-persistent";
import { createLocalStorageDriver } from "@cubux/storage-driver";

const $accessToken = withPersistent(
  createStore(""),
  createLocalStorageDriver(),
  "accessToken"
);
```

## Install

```sh
npm i @cubux/effector-persistent
```

## API

See also [`@cubux/storage-driver`](https://github.com/cubux-net/ts-storage-driver).
It supports `localStorage`/`sessionStorage` and `indexedDB`.

### `withPersistent()`

Register persistent data handling for the given store with the given driver.
Data from `store` will be stored in `driver` with the given `key`. Function
returns input `store`, so it can be used inline.

Type of `store` can be `Store` only when `wakeUp` options is defined. Otherwise
a `StoreWritable` is needed.

```ts
function withPersistent<Key, Value, Serialized = Value>(
  store:    Store<Value>
          | StoreWritable<Value>,
  driver:   StoreDriverSingle<Key, Serialized>
          | Promise<StoreDriverSingle<Key, Serialized>>,
  key:      Key,
  options?: WithPersistentOptions<Value, Value, Serialized>
): typeof store
```

Example:

```ts
import { createStore } from "effector";
import { withPersistent } from "@cubux/effector-persistent";
import { createLocalStorageDriver } from "@cubux/storage-driver";

const lsDriver = createLocalStorageDriver();
const $storeA = withPersistent(createStore(0), lsDriver, "keyA");
const $storeB = withPersistent(createStore(""), lsDriver, "keyB");
```

In the example above `$storeA` and `$storeB` will use `localStorage` for
persistent data with keys `"persistent:keyA"` and `"persistent:keyB"`
respectively.

### `withPersistentMap()`

Register persistent data handling for the given `ReadonlyMap` store with the
given driver. Data from `store` will be stored in `driver` with corresponding
keys from `ReadonlyMap`. Function returns input `store`, so it can be used
inline.

Type of `store` can be `Store` only when `wakeUp` options is defined. Otherwise
a `StoreWritable` is needed.

```ts
function withPersistentMap<Key, Value, Serialized = Value>(
  store:    Store<ReadonlyMap<Key, Value>>
          | StoreWritable<ReadonlyMap<Key, Value>>,
  driver:   StoreDriver<Key, Serialized>
          | Promise<StoreDriver<Key, Serialized>>,
  options?: WithPersistentOptions<ReadonlyMap<Key, Value>, Value, Serialized>
): typeof store
```

Under the hood on every `store` change it will detect changes in underlying
`Map` entries and will send to `driver` only those was changed.

**Notice:** Serialization when used with `options` will be applied to individual
values `Value` rather than to whole `Map<Key, Value>`.

Example:

```ts
import { createStore } from "effector";
import { withPersistentMap } from "@cubux/effector-persistent";
import { createLocalStorageDriver } from "@cubux/storage-driver";

const $storeMap = withPersistentMap(
  createStore<ReadonlyMap<string, number>>(new Map()),
  createLocalStorageDriver()
);
```

In the example above `$storeMap` will use `localStorage` for persistent data
with keys starting with `"persistent:"`, so every entry from `ReadonlyMap` will
have its own row in `localStorage`.

### `interface WithPersistentFlushEvent`

A payload for flush events.

| Property | Type     | Description                       |
|----------|----------|-----------------------------------|
| `id`     | `symbol` | Identifier for current flush flow |

### `interface WithPersistentFlushFailEvent`

A payload for flush events.

This interface extends `WithPersistentFlushEvent` with the following additional
properties:

| Property | Type      | Description           |
|----------|-----------|-----------------------|
| `error`  | `unknown` | Reason of the failure |

### `interface WithPersistentOptions`

Common options for persistent storage.

```ts
interface WithPersistentOptions<
  State = any,
  Value = State,
  Serialized = Value
>
```

| Options          | Type                                                                          | Default     | Description                                                                                                                                                                                      |
|------------------|-------------------------------------------------------------------------------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `flushDelay`     | `number`                                                                      | `undefined` | Debounce subsequent store updates and flush only after latest change. If set to `undefined` (default), no debounce will be used, so every store update will be flushed to driver.                |
| `onFlushStart`   | `EventCallable<WithPersistentFlushEvent>`                                     | `undefined` | An Event to trigger before flushing to driver. An `id` in payload can be used in all the rest "flush" events to identify the flow when it's needed.                                              |
| `onFlushDone`    | `EventCallable<WithPersistentFlushEvent>`                                     | `undefined` | An Event to trigger when flush succeeds. An `id` in payload refers to `id` from appropriate `onFlushStart` payload.                                                                              |
| `onFlushFail`    | `EventCallable<WithPersistentFlushFailEvent>`                                 | `undefined` | An Event to trigger when flush fails. An `id` in payload refers to `id` from appropriate `onFlushStart` payload.                                                                                 |
| `onFlushFinally` | `EventCallable<WithPersistentFlushEvent>`                                     | `undefined` | An Event to trigger before flushing to driver. This al always triggering after either `onFlushDone` or `onFlushFail`. An `id` in payload refers to `id` from appropriate `onFlushStart` payload. |
| `readOnly`       | `EventCallable<boolean>`                                                      | `undefined` | A `filter` Store to disable writes to Driver.                                                                                                                                                    |
| `wakeUp`         | <code>StoreWritable&lt;State&gt; &#124; ((state: State) =&gt; void)</code>    | `undefined` | Alternative target which will receive initial state read from driver on initialization. When `undefined`, the source StoreWritable will be used.                                                 |
| `onBeforeWakeUp` | `() => void`                                                                  | `undefined` | A callback to be called before "wake up" prodecure.                                                                                                                                              |
| `onAfterWakeUp`  | `() => void`                                                                  | `undefined` | A callback to be called after "wake up" prodecure.                                                                                                                                               |
| `serialize`      | <code>(input: Value) =&gt; Serialized &#124; Promise&lt;Serialized&gt;</code> | `undefined` | Serialization before writing data to driver.                                                                                                                                                     |
| `unserialize`    | <code>(output: Serialized) =&gt; Value &#124; Promise&lt;Value&gt;</code>     | `undefined` | Unserialization after reading data from driver.                                                                                                                                                  |

## Helper API

### `flushDelayed()`

Setup delayed flushes from `source` unit to `target` unit with whe given debounce
interval.

Prefer [`patronum` `debounce()`](https://patronum.effector.dev/methods/debounce/)
instead.

```ts
function flushDelayed<T>(options: Options<T>): (() => void);
```

It takes options described below and returns a function to stop watching and
interrupt planned flush.

| Options      | Type                                              | Default      | Description                                                               |
|--------------|---------------------------------------------------|--------------|---------------------------------------------------------------------------|
| `source`     | <code>Store&lt;T&gt; &#124; Event&lt;T&gt;</code> | **Required** | Source unit to watch                                                      |
| `target`     | `(payload: T) => void`                            | **Required** | Receiver to flush data to                                                 |
| `flushDelay` | <code>number &#124; Store&lt;number&gt;</code>    | `1000`       | Debounce timeout to await before flush                                    |
| `filter`     | <code>Store&lt;boolean&gt;</code>                 | `undefined`  | When specified, flushes will work only when this filter `Store` is `true` |

Actual flush `target` will be called only after `source` unit will stop
triggering for at least duration in `flushDelay`. That is while `source` is
keeping triggering continuous within this duration, flush `target` will never
be called.

When `flushDelay` is `Store<number>`, it's value will take effect only on next
`source` update.

When `filter` is used, it will abort current planned flush, when its value
becomes `false`.

---
_Source: https://npm.io/package/@cubux/effector-persistent · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
