Atom
Framework-agnostic state, the way it should be. An atom isn’t just a value — it’s a value with action methods bound to it. Call domain verbs (cartAtom.push(item), authAtom.login(creds), sidebarAtom.toggle()) instead of writing free-standing setters everywhere. Works in any JS/TS environment — React, Vue, Node, vanilla.
Highlighted features
Values with verbs
Action methods live on the atom — sidebar.toggle(), not setSidebar(!sidebar.value). Bound this, named intent.
Derived values, auto-tracked
derive(“fullName”, get => — dependency graph rebuilt on each read, conditional reads work, chained derives propagate.${get(first)} ${get(last)})
Persistence built in
persist: true for localStorage, or any custom PersistAdapter (cookies, IndexedDB, @mongez/cache, …). Restore on construction, write on every update.
SSR-isolated stores
AtomStore + createAtomStore for per-request isolation. Module-level singletons stay safe; per-user state stays scoped.
Redux DevTools time-travel
enableAtomDevtools() bridges to the Redux DevTools extension — full action history + jump-to-state. Tree-shaken when never imported.
Specialized factories
atomCollection for arrays (adds push/pop/remove/map), derive for computed values — the common shapes don’t need rebuilding.
Install
npm install @mongez/atomyarn add @mongez/atompnpm add @mongez/atomPeer deps installed automatically: @mongez/events, @mongez/reinforcements.
Quick peek
import { createAtom } from "@mongez/atom";
const sidebarAtom = createAtom({ key: "ui.sidebar", default: false, actions: { open() { this.update(true); }, close() { this.update(false); }, toggle() { this.update(!this.value); }, },});
sidebarAtom.toggle(); // no setSidebar(!sidebar.value) ceremonysidebarAtom.value; // trueAtoms aren’t just values — they’re values with verbs bound to them. Call domain methods directly on the atom instead of writing setters everywhere. Naming convention: suffix atom variables with Atom (e.g. counterAtom, sidebarAtom) to avoid clashes with component props.
Mental model
All atoms live in a module-level registry (atoms object exported from the package). Each atom is keyed by the key string passed to createAtom. Keys should be namespaced with dots: "ui.sidebar", "cart", "user.profile".
sidebarAtom.toggle(); // not: setSidebar(!sidebar.value)cartAtom.push(item); // not: setCart([...cart.value, item])authAtom.login(creds); // not: dispatch({ type: "AUTH_LOGIN", payload: creds })Package hierarchy
| Package | Role |
|---|---|
@mongez/atom | Core. Framework-agnostic atom factory, SSR isolation, persistence, DevTools. |
@mongez/react-atom | React adapter. Per-atom hooks (useValue, useState, use), <AtomStoreProvider>, preset atoms. |
@mongez/atomic-query | Server-state cache on top of atoms. useQuery, useMutation, useInfiniteQuery. |
Rule of thumb: @mongez/atom for shared, UI-independent logic. @mongez/react-atom for anything that drives component re-renders. @mongez/atomic-query for async server data.
Lifecycle events
Every atom emits on the @mongez/events bus under atoms.${key}:
| Event | Fired by |
|---|---|
atoms.${key}.update | update(), change(), merge() |
atoms.${key}.reset | reset(), silentReset() |
atoms.${key}.delete | destroy() |
The namespace is segment-aware: destroying users.1 does not match users.10.
Key pitfalls
- Key collisions are global. If two
createAtomcalls share the same key, the second overwrites the first. Prefix keys by domain ("ui.sidebar", not"sidebar"). - No reference equality shortcut for objects.
update()short-circuits only when the new value=== currentValue. For object atoms, always pass a new reference or usemerge()/change(). - No React here.
useAtomlives in@mongez/react-atom, not this package.
Where to go next
- Atoms, Defining atoms, Actions — the core API
- Derived atoms, Atom collections — specialised shapes
- Persistence —
persist: trueand customPersistAdapter - Atom stores (SSR) — per-request isolation
- Devtools — Redux DevTools time-travel
- Recipes — cross-feature compositions