Overview

Mates state is built around atom — a callable reactive cell. Read with count() inside templates, memos, and effects; write with .set() / .update(). Same-value writes still notify.

Specialize from there: iAtom for deep-frozen configs, atom.number / atom.array / atom.list for helpers, asyncAtom for keyed fetches, and molecule to walk nested atom bags. Reach for useState / store when state is naturally an object with methods, and scope to share down the tree without prop drilling.

Create atoms in the component outer function. SPA module-level atoms are shared client singletons; SSR App.ts module scope throws. Never inside the template. See Restricted Usage.

Choose a primitive

API
Role
Prefer when
atom Core cell Default reactive value — counters, fields, small objects.
iAtom / signal Frozen Configs / payloads where in-place mutation must fail.
atom.numberatom.object Typed helpers Domain methods (incr, toggle, push, setItem, …).
atom.list / Map / Set / stack Collections Keyed lists, native Map/Set, LIFO/FIFO.
asyncAtom Async resource Keyed fetch with status / loading / refetch.
useState / store Object state Local vs module-level bags with methods.
memo / effect Derived / effects Computed values and reactive side effects.

Default: plain atom

Most UI state starts as atom(value). Read in the template; set from events.

import { html, atom, renderApp } from 'mates';

const App = () => {
const count = atom(0);
return () => html`
<div class="m-col m-gap">
<h1>${count()}</h1>
<div class="m-flex m-items-center m-justify-center m-gap-sm">
<button class="btn-primary" @click=${() => count.set(n => n + 1)}>+1</button>
<button @click=${() => count.set(0)}>Reset</button>
</div>
</div>
`;
};

renderApp(App, document.getElementById('app'));

Typed factories (preview)

atom.number / atom.bool / atom.string add small helpers on top of the same atom engine. Full API on Typed atom.* factories.

import { html, atom, renderApp } from 'mates';

const App = () => {
const n = atom.number(0);
const on = atom.bool(false);
const label = atom.string('off');

return () => html`
<div class="m-col m-gap">
<h1>${n()}</h1>
<p class="muted m-t-5">${label()} · bool=${on()}</p>
<div class="m-flex m-items-center m-justify-center m-gap-sm">
<button class="btn-primary" @click=${() => n.incr()}>incr</button>
<button @click=${() => {
on.toggle();
label.set(on() ? 'on' : 'off');
}}>toggle</button>
</div>
</div>
`;
};

renderApp(App, document.getElementById('app'));