use-json-localstorage
v1.4.1
Published
JSON local storage for React
Readme
use-json-localstorage
A tiny, React 18+ safe localStorage utility built on top of useSyncExternalStore.
Most
useLocalStoragehooks rely onuseEffect + useState, which can be unsafe in concurrent rendering. This library follows the official React pattern for subscribing to external stores.
Why
React 18 introduced concurrent rendering. In this environment, the common pattern below is no longer safe:
useEffect(() => {
store.subscribe(setState);
}, []);This can lead to:
- tearing (inconsistent state during render)
- unexpected double renders in Strict Mode
- subtle bugs that are hard to reproduce
React provides a solution for this exact problem:
useSyncExternalStoreuse-json-localstorage is a small abstraction that applies this API to localStorage.
Features
- ✅ React 18+ safe (
useSyncExternalStore) - ✅ JSON-based persistence (via
superjson) - ✅ Subscribable localStorage updates
- ✅ Type-safe API
- ✅ Tiny & opinionated
Non-goals
This library does NOT aim to be:
- a global state manager (Redux, Zustand, etc.)
- an ORM or database abstraction
- a complex query engine
- a replacement for server-side persistence
It is intentionally small.
Install
npm install use-json-localstorageReact 18 or higher is required.
Basic Usage
import { useLocalStorageValue } from 'use-json-localstorage';
function Counter() {
const count = useLocalStorageValue('count', 0);
return <div>{count}</div>;
}countis read fromlocalStorage- updates are subscribed via
useSyncExternalStore - React always receives a consistent snapshot
Writing Values
import { getRuntimeLocalStorage } from 'use-json-localstorage';
getRuntimeLocalStorage().set('count', 1);All subscribers of the same key will be notified.
Supported Data Types
Serialization is handled by superjson, which means the following are supported:
ObjectArrayDateMapSetBigInt
How It Works
At a high level:
localStorageis wrapped as an evented external store- React subscribes via
useSyncExternalStore - Updates trigger a re-render using a fresh snapshot
localStorage
↓
event emitter
↓
useSyncExternalStore
↓
React renderThis follows the same pattern used internally by modern state libraries.
Example: Mutation-style Update
getRuntimeLocalStorage().set('user', {
id: '1',
name: 'Dohyeon',
});You can build schema-based mutation helpers on top of this primitive.
SSR
This library is client-only.
- No SSR persistence
- No server synchronization
License
MIT
Motivation
This project was created to explore:
- React 18 concurrency-safe patterns
- External store design
- A minimal, explainable alternative to ad-hoc
useLocalStoragehooks
If you are building UI infrastructure or headless utilities, this pattern may be useful.
Credits
Inspired by the React 18 RFC and the design of modern state libraries.
License
MIT
