Skip to content

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 => ${get(first)} ${get(last)}) — dependency graph rebuilt on each read, conditional reads work, chained derives propagate.

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

Terminal window
npm install @mongez/atom

Peer 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) ceremony
sidebarAtom.value; // true

Atoms 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

PackageRole
@mongez/atomCore. Framework-agnostic atom factory, SSR isolation, persistence, DevTools.
@mongez/react-atomReact adapter. Per-atom hooks (useValue, useState, use), <AtomStoreProvider>, preset atoms.
@mongez/atomic-queryServer-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}:

EventFired by
atoms.${key}.updateupdate(), change(), merge()
atoms.${key}.resetreset(), silentReset()
atoms.${key}.deletedestroy()

The namespace is segment-aware: destroying users.1 does not match users.10.

Key pitfalls

  • Key collisions are global. If two createAtom calls 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 use merge() / change().
  • No React here. useAtom lives in @mongez/react-atom, not this package.

Where to go next