Overview

setter wraps a plain function so that calling it also notifies subscribers and schedules the host component to re-render. Your state stays as ordinary let variables — there is no atom, no dependency tracking, and no per-read subscription cost.

Aliases $ and _ are identical to setter. Use them when you want a shorter call site; behavior is the same.

This is the lightest way to drive UI from local mutations. Prefer atom when you need derived values, effects, or shared reactive reads across components.

Compared to atom

Member
Status
Notes
let count + setter manual notify Re-renders only when a setter is called. Reads are not tracked.
atom(0) reactive Reads subscribe templates/effects; .set / .update notify automatically.

setter — plain local state

Plain let variables are not reactive. setter() wraps any mutation function and schedules a component re-render after the mutation runs — no atom overhead needed.

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

const App = () => {
// Plain local variables — NOT reactive atoms
let count = 0;
let step = 1;

// setter() wraps a mutation function and triggers a re-render
const increment = setter(() => { count += step; });
const decrement = setter(() => { count -= step; });
const reset = setter(() => { count = 0; });
const setStep = setter((n) => { step = n; });

return () => html`
<div class="m-col m-gap">
<div class="center">
<h1>${count}</h1>
</div>
<div class="m-flex m-items-center m-justify-center m-gap-sm">
<button class="btn-danger" @click=${decrement}>−${step}</button>
<button @click=${reset}>Reset</button>
<button class="btn-primary" @click=${increment}>+${step}</button>
</div>
<div class="m-flex m-items-center m-justify-center m-gap-sm">
<span class="label">Step:</span>
${[1,5,10].map(n => html`
<button class=${step === n ? 'btn-primary' : 'btn-ghost'} @click=${() => setStep(n)}>${n}</button>
`)}
</div>
</div>
`;
};
renderApp(App, document.getElementById('app'));

setter.set() — swap the fn

Keep the same callable identity (handlers stay valid) while swapping the underlying mutation with .set(). $ is an alias for setter.

import { html, setter, $, renderApp } from 'mates';

const App = () => {
let count = 0;
let mode = 'by1';

const bump = setter(() => { count += 1; });
const setMode = $((m) => {
mode = m;
bump.set(m === 'by10'
? () => { count += 10; }
: () => { count += 1; });
});

return () => html`
<div class="m-col m-gap">
<h1>${count}</h1>
<p class="muted m-t-5">Mode: ${mode} (same bump() ref)</p>
<div class="m-flex m-items-center m-justify-center m-gap-sm">
<button class="btn-primary" @click=${bump}>bump</button>
<button class=${mode === 'by1' ? 'btn-primary' : 'btn-ghost'}
@click=${() => setMode('by1')}>+1</button>
<button class=${mode === 'by10' ? 'btn-primary' : 'btn-ghost'}
@click=${() => setMode('by10')}>+10</button>
</div>
</div>
`;
};
renderApp(App, document.getElementById('app'));