mobx-persist-store

A lightweight utility that persists and rehydrates MobX observable store properties to any storage backend.

Library
npm
v1.1.8
293stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
38/100Needs Attention
Development Activity0
Maintenance32
Community40
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
62/100Good
Architecture68
Code Quality62
Innovation62
Learning Curve55

mobx-persist-store is a small utility library for automatically persisting and rehydrating observable properties in MobX stores. Instead of hand-rolling serialization logic for every store, you call makePersistable once with a list of properties to watch, a name to use as the storage key, and a storage adapter (localStorage, sessionStorage, AsyncStorage, localForage, or any object matching the same getItem/setItem/removeItem interface), and the library sets up a MobX reaction that writes changes to storage automatically and rehydrates them on the next load.

It ships built-in support for ES6 Map and Set observables (auto-serialized to arrays and restored on hydration), configurable expiration and versioning so stale persisted data is dropped instead of silently corrupting a store’s shape, and a small API — isHydrated, isPersisting, pausePersisting, startPersisting, stopPersisting, clearPersistedStore, getPersistedStore — for controlling persistence at runtime. A global configurePersistable call lets you set default storage/expiration/reaction options once instead of repeating them on every store.

What You Get

  • makePersistable API - one function call that wires hydration and persistence for a set of named observable properties on any MobX store.
  • Pluggable storage adapters - works with localStorage, sessionStorage, AsyncStorage, localForage, or any object exposing getItem/setItem/removeItem.
  • Built-in Map/Set serialization - ObservableMap and ObservableSet properties are automatically converted to arrays for storage and rebuilt on hydration.
  • Expiration and versioning - optional expireIn and version options drop stale persisted data automatically instead of rehydrating a store into an inconsistent shape.
  • Runtime persistence controls - isHydrated, isPersisting, pausePersisting, startPersisting, and stopPersisting let you pause/resume/tear down persistence for stores that don’t live for the app’s full lifetime.

Common Use Cases

  • Remembering user preferences - persist theme, layout, or filter selections in a settings store so they survive a page reload.
  • React Native app state - persist MobX stores to AsyncStorage so onboarding progress, auth tokens, or cart contents survive an app restart.
  • Offline-first draft data - keep form or editor drafts in a store that’s persisted to localStorage/localForage so in-progress work isn’t lost on refresh.
  • Session-scoped caches - persist a store to sessionStorage with an expireIn so cached data is automatically invalidated after a set period.

Under The Hood

Architecture mobx-persist-store centers on a PersistStore class that pairs a target MobX-observable object with a StorageAdapter and a list of watched properties; makePersistable constructs one PersistStore per call and tracks it in a shared PersistStoreMap keyed by the target instance, guarding against duplicate registrations for the same storage name. Hydration reads the raw payload from the storage adapter and reassigns each watched property back onto the target inside a MobX runInAction, with dedicated branches for restoring ObservableMap and ObservableSet values from their serialized array form. Persistence is driven by a single MobX reaction that recomputes a plain object of serialized property values and writes it to storage on change, with pause/stop/start methods controlling that reaction’s lifecycle. The design is flat and directly wired rather than layered through abstractions or a DI container, which keeps the surface easy to trace but means the Map/Set-specific serialization branches live inline in the reaction and hydration paths rather than behind a pluggable per-type strategy.

Tech Stack The library is authored in strict-mode TypeScript with no runtime dependencies of its own beyond a peer dependency on mobx, and compiles through plain tsc into separate module targets (ES5, ES2015, ES2017) rather than a bundler, favoring broad compatibility across bundlers and React Native over a modern build pipeline. Tests run under Jest with ts-jest and a Babel-based transform toolchain, plus a jest-localstorage-mock package for exercising the browser storage path, and a small milliseconds helper for building expiration test fixtures. Continuous integration is limited to a single release-focused GitHub Actions workflow rather than a broader per-PR test gate.

Code Quality The test suite exercises the adapter’s stringify/parse round trip, expiration and version-mismatch invalidation, global configuration, and small utility predicates with realistic mocked storage rather than superficial smoke tests, and strict TypeScript catches a comprehensive class of type errors at compile time. Error handling favors safe fallbacks — JSON parsing failures in the storage adapter fall back to the raw value rather than throwing — and naming is consistent, with public function names matching their source filenames one-to-one. The project has no dedicated linter configuration beyond a bare Prettier config and a tsc —noEmit type-check script, and no visible CI job that runs the test suite on pull requests, which limits confidence for external contributors relative to the depth of the tests themselves.

API Design The entire public surface reduces to one primary entry point, makePersistable, plus a handful of narrowly-scoped helpers (isHydrated, pausePersisting, clearPersistedStore, and similar) that most consumers never need to touch directly — an unusually minimal API for a persistence library compared to more decorator- or class-heavy alternatives. Storage adapters are accepted in the same shape as the native Web Storage interface, so localStorage, AsyncStorage, and third-party stores like localForage all work without an adapter wrapper, and Map/Set observables are serialized and restored automatically rather than requiring manual conversion. Development-mode console warnings flag the two most common misuse patterns — registering a duplicate storage name and passing a storage object missing one of getItem/setItem/removeItem — a thoughtful touch given the library’s otherwise minimal surface; the underlying pattern of a MobX reaction backed by a pluggable storage adapter is a well-established approach rather than a novel one, but it’s executed with above-average developer ergonomics and documentation depth.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search