Skip to content

Repository files navigation

Valdres

Reactive state management for React, Vue, Svelte, Solid, and Angular — one store, shared across frameworks. Inspired by Recoil and Jotai. The framework-agnostic core also runs in plain JavaScript, Node, and workers.

Docs: valdres.dev · AI-readable: llms.txt (every page is also available as markdown — append .md to any URL)

npm install valdres valdres-react   # or valdres-vue / -svelte / -solid / -angular
import { atom, selector } from "valdres"
import { Provider, useAtom, useValue } from "valdres-react"

const countAtom = atom(0)
const doubledSelector = selector(get => get(countAtom) * 2)

function Counter() {
    const [count, setCount] = useAtom(countAtom)
    const doubled = useValue(doubledSelector)
    return <button onClick={() => setCount(c => c + 1)}>{count} ×2 = {doubled}</button>
}

const App = () => (
    <Provider>
        <Counter />
    </Provider>
)

Atoms and selectors are identified by reference (no string keys), families are first-class, transactions batch updates, and scoped stores fork state for edit-and-cancel flows. The same atoms work in every framework — and in the plugin packages that wrap browser APIs (geolocation, keyboard, visibility, …) as reactive state.

Packages

The package tables below are auto-generated — do not hand-edit.

Core

Package Description
valdres Fast atom-based state management for JavaScript. Inspired by Recoil and Jotai.

Framework adapters

Package Description
valdres-angular Angular bindings for Valdres state management
valdres-react React bindings for Valdres state management
valdres-solid Solid.js bindings for Valdres state management
valdres-svelte Svelte 5 bindings for Valdres state management
valdres-vue Vue 3 bindings for Valdres state management

Plugins (framework-agnostic)

Package Description
@valdres/bandwidth Reactive network throughput, latency, and jitter from a live measurement
@valdres/browser-color-scheme Reactive prefers-color-scheme (dark / light) media query
@valdres/browser-contrast Reactive prefers-contrast preference from the OS
@valdres/browser-device-motion Reactive device acceleration and rotation from DeviceMotionEvent
@valdres/browser-device-orientation Reactive device tilt and compass heading from DeviceOrientationEvent
@valdres/browser-focus Reactive document focus state from the window focus/blur events
@valdres/browser-geolocation Reactive geolocation — position, accuracy, permission, and status
@valdres/browser-keyboard Reactive pressed-key state from keydown / keyup
@valdres/browser-online Reactive online/offline status from navigator.onLine
@valdres/browser-presence Whether the user is present — tab visible and window focused
@valdres/browser-reduced-data Reactive prefers-reduced-data media query
@valdres/browser-reduced-motion Reactive prefers-reduced-motion preference from matchMedia
@valdres/browser-reduced-transparency Reactive prefers-reduced-transparency media query state
@valdres/browser-screen Reactive screen resolution and orientation from window.screen
@valdres/browser-screen-details Reactive multi-screen layout from the Window Management API
@valdres/browser-visibility Reactive Page Visibility state (visible / hidden)
@valdres/browser-window Reactive window inner size, tracked through resize events
@valdres/color-mode Color mode (dark/light theme) state powered by Valdres
@valdres/hotkeys Hotkey state management powered by Valdres
@valdres/public-ip Reactive public IP (v4/v6) with stale-while-revalidate
@valdres/redux-devtools Connect a valdres store to the Redux DevTools browser extension

React extras

Package Description
@valdres-react/color-mode React color mode hooks powered by Valdres
@valdres-react/draggable React drag-and-drop powered by Valdres
@valdres-react/hotkeys React hotkey hooks powered by Valdres
@valdres-react/jotai Jotai API compatibility layer for Valdres
@valdres-react/panable React pan and zoom powered by Valdres
@valdres-react/recoil Recoil API compatibility layer for Valdres

Development

bun install
bun test            # all packages
bun run docs:dev    # docs site at localhost:4321

Releasing

Versioning and publishing is handled by Changesets. Each package versions independently.

When you open a PR that changes a publishable package:

bunx changeset

Pick the affected packages, the bump type (patch/minor/major), and write a short summary. Commit the generated .changeset/*.md file with your PR.

For PRs that touch publishable code but intentionally don't trigger a release (refactors, internal cleanup, docs):

bunx changeset --empty

This still generates a .changeset/*.md file — commit it like a regular changeset. The Require changeset check on each PR enforces that any change to a publishable package ships with a changeset (empty or otherwise).

When the PR merges to main, the Publish workflow opens (or updates) a Version Packages PR that applies the pending changesets, bumps versions, and updates CHANGELOGs. Merging that PR publishes the affected packages to npm.

To preview what publishing would do locally:

bun run verify-publish

The repo is currently in beta prerelease mode (bunx changeset pre exit to graduate to stable).

Benchmarks

Performance

valdres is benchmarked against Jotai (and a raw Map floor) on every PR via Bencher — live, always-current latency per operation under both Bun (JavaScriptCore) and Node.js (V8):

bencher.dev/perf/valdres

store.get(atom) latency — valdres vs Jotai vs raw Map (Bun + Node)

Live plot — store.get(atom) latency, valdres vs Jotai vs a raw Map floor, on both runtimes. Auto-updates from main; click through to filter/zoom. (Sparse until main accumulates a few runs.)

Every PR from the repo gets a comment flagging any latency regression vs main (fork PRs are skipped — they can't read the upload key).

Performance vs Jotai

Latest main latency per operation (live, always-current numbers: bencher.dev/perf/valdres). Auto-generated from Bencher — do not hand-edit.

Bun (JavaScriptCore)

Operation valdres Jotai
atom lifecycle (create+100get+100set) 9.2µs 155.1µs 🟢 16.8× faster
atom(1) 2ns 43ns 🟢 23.4× faster
atomFamily: txn update 5,000 existing members 1.92ms 7.43ms 🟢 3.9× faster
atomFamily(id) 180ns 227ns 🟢 1.3× faster
atomFamily(id) cache hit 13ns 9ns 🔴 1.5× slower
atomFamily(string) cache hit 23ns 19ns 🔴 1.2× slower
createStore 279ns 4.2µs 🟢 15.2× faster
get 1000 atoms 9.4µs 444.2µs 🟢 47.0× faster
selector(fn) 6ns 45ns 🟢 8.0× faster
selectorFamily(id) 220ns 329ns 🟢 1.5× faster
selectorFamily(string) cache hit 31ns 14ns 🔴 2.2× slower
set + read 10 selectors 5.5µs 23.2µs 🟢 4.2× faster
set + read 100 selectorFamily entries 65.4µs 169.4µs 🟢 2.6× faster
set + read 100 selectors 52.6µs 250.5µs 🟢 4.8× faster
set + read through 5 chained selectors 3.8µs 9.9µs 🟢 2.6× faster
set 1000 atoms 74.7µs 613.9µs 🟢 8.2× faster
set(atom, curr => curr+1) 100ns 1.9µs 🟢 19.2× faster
set(atom, value) 110ns 1.1µs 🟢 10.4× faster
set(atom) with 10 subs 138ns 1.9µs 🟢 13.5× faster
store.get(atom) 30ns 231ns 🟢 7.7× faster
sub + unsub 275ns 1.5µs 🟢 5.4× faster
sub+unsub on chain of 100 unsubscribed derived deps 77.9µs 94.5µs 🟢 1.2× faster
sub+unsub on chain of 50 unsubscribed derived deps 47.6µs 51.0µs 🟢 1.1× faster
sub+unsub on chain of 500 unsubscribed derived deps 353.0µs 453.1µs 🟢 1.3× faster
subscribe + unsubscribe 100 shared selector pairs 522.4µs 602.3µs 🟢 1.2× faster
subscribe + unsubscribe 100 shared selector pairs + fan-in 488.2µs 631.3µs 🟢 1.3× faster
subscribe + unsubscribe 100 shared selector pairs + fan-in + mounted spine 1.14ms 781.0µs 🔴 1.5× slower
txn: 10 atoms × 10 selectors, set + read 80.0µs 250.9µs 🟢 3.1× faster
txn: 10 atoms × 10 selectors, with subs 77.0µs 380.6µs 🟢 4.9× faster
txn: 10 atoms × 100 selectors, set + read 729.5µs 2.43ms 🟢 3.3× faster
txn: asymmetric DAG shared sink 22.3µs 75.6µs 🟢 3.4× faster
txn: cross-atom 1000 selectors, set + read 894.2µs 2.50ms 🟢 2.8× faster
txn: cross-atom 1000 selectors, with subs 836.8µs 13.25ms 🟢 15.8× faster
txn: large asymmetric DAG (1000 leaves × 50 chain) 2.59ms 9.84ms 🟢 3.8× faster

Node.js (V8)

Operation valdres Jotai
atom lifecycle (create+100get+100set) 27.6µs 89.4µs 🟢 3.2× faster
atom(1) 19ns 39ns 🟢 2.0× faster
atomFamily: txn update 5,000 existing members 3.00ms 4.02ms 🟢 1.3× faster
atomFamily(id) 119ns 175ns 🟢 1.5× faster
atomFamily(id) cache hit 107ns 13ns 🔴 8.2× slower
atomFamily(string) cache hit 110ns 13ns 🔴 8.7× slower
createStore 396ns 532ns 🟢 1.3× faster
get 1000 atoms 15.8µs 120.0µs 🟢 7.6× faster
selector(fn) 33ns 163ns 🟢 4.9× faster
selectorFamily(id) 1.1µs 577ns 🔴 1.9× slower
selectorFamily(string) cache hit 70ns 21ns 🔴 3.3× slower
set + read 10 selectors 9.2µs 13.6µs 🟢 1.5× faster
set + read 100 selectorFamily entries 89.6µs 102.2µs 🟢 1.1× faster
set + read 100 selectors 89.3µs 91.0µs 🟢 1.0× faster
set + read through 5 chained selectors 5.1µs 7.7µs 🟢 1.5× faster
set 1000 atoms 89.6µs 249.5µs 🟢 2.8× faster
set(atom, curr => curr+1) 250ns 942ns 🟢 3.8× faster
set(atom, value) 237ns 779ns 🟢 3.3× faster
set(atom) with 10 subs 323ns 1.1µs 🟢 3.5× faster
store.get(atom) 18ns 114ns 🟢 6.2× faster
sub + unsub 666ns 702ns 🟢 1.1× faster
sub+unsub on chain of 100 unsubscribed derived deps 129.4µs 77.3µs 🔴 1.7× slower
sub+unsub on chain of 50 unsubscribed derived deps 79.5µs 70.8µs 🔴 1.1× slower
sub+unsub on chain of 500 unsubscribed derived deps 597.3µs 378.0µs 🔴 1.6× slower
subscribe + unsubscribe 100 shared selector pairs 715.5µs 333.2µs 🔴 2.1× slower
subscribe + unsubscribe 100 shared selector pairs + fan-in 690.3µs 360.5µs 🔴 1.9× slower
subscribe + unsubscribe 100 shared selector pairs + fan-in + mounted spine 1.32ms 370.3µs 🔴 3.6× slower
txn: 10 atoms × 10 selectors, set + read 106.0µs 109.6µs 🟢 1.0× faster
txn: 10 atoms × 10 selectors, with subs 68.4µs 161.5µs 🟢 2.4× faster
txn: 10 atoms × 100 selectors, set + read 1.02ms 958.3µs 🔴 1.1× slower
txn: asymmetric DAG shared sink 20.3µs 41.8µs 🟢 2.1× faster
txn: cross-atom 1000 selectors, set + read 1.25ms 1.30ms 🟢 1.0× faster
txn: cross-atom 1000 selectors, with subs 773.5µs 6.95ms 🟢 9.0× faster
txn: large asymmetric DAG (1000 leaves × 50 chain) 2.43ms 4.53ms 🟢 1.9× faster

About

Fast reactive state management for JavaScript and React

Topics

Resources

Stars

4 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages