Overview

These helpers extend the core atom model for two common needs: timed writes (debouncedAtom) and a reactive page index (paginationAtom).

debouncedAtom(sourceOrValue, delay) accepts either a plain initial value or a source atom. Writes (or mirrored source changes) wait until delay ms of quiet — only the last value in the window commits. Timer cleanup is registered automatically.

paginationAtom(initial?) is a full AtomType<number> plus next(), prev() (floors at 1), reset(), and a currentPage alias — not a separate pageSize/totalPages object.

Browser persistence bags (lsStore / ssStore) live on useUtils() — see Storage. Theme and document title are covered under themeAtom / titleAtom.

Which helper

Helper
Role
Notes
debouncedAtom timed writes Last write in the delay window fires; good for search / autosave.
paginationAtom page number Atom<number> helpers — use with your own total/pageSize logic.
lsStore / ssStore persist Dedicated page — /docs/state/storage.

Pagination cheatsheet

// paginationAtom — AtomType<number> helpers
const page = paginationAtom(1);
page.next();  // 2
page.prev();  // 1 (never below 1)
page.set(10); // jump
page.reset(); // back to 1

// Persistence → useUtils().lsStore / ssStore
// See /docs/state/storage

debouncedAtom — debounced search

The raw query atom updates on every keystroke, but the debounced atom only propagates changes after the user stops typing for 400 ms — ideal for search inputs that trigger API calls.

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

const App = () => {
const query = atom('');
// Only updates debounced after 400ms of no typing
const debounced = debouncedAtom(query, 400);

return () => html`
<div class="m-col m-gap">
<input
.value=${query()}
@input=${e => query.set(e.target.value)}
placeholder="Type to search..."
/>
<div class="m-grid m-grid-cols-3 m-gap">
<div class="card card-body center">
<span class="overline">Typed</span>
<span class="card-title">${query() || '—'}</span>
</div>
<div class="card card-body center">
<span class="overline">Debounced (400ms)</span>
<span class="card-title">${debounced() || '—'}</span>
</div>
</div>
</div>
`;
};
renderApp(App, document.getElementById('app'));

paginationAtom — page index

next / prev / reset helpers on a normal number atom. prev() floors at 1; pair with your own totalPages math.

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

const App = () => {
const page = paginationAtom(1);
const totalPages = 5;

return () => html`
<div class="m-col m-gap">
<h1>${page()}</h1>
<p class="muted m-t-5">Page ${page.currentPage} / ${totalPages}</p>
<div class="m-flex m-items-center m-justify-center m-gap-sm">
<button @click=${() => page.prev()} ?disabled=${page() <= 1}>Prev</button>
<button class="btn-primary" @click=${() => {
if (page() < totalPages) page.next();
}} ?disabled=${page() >= totalPages}>Next</button>
<button class="btn-ghost" @click=${() => page.reset()}>Reset</button>
</div>
</div>
`;
};
renderApp(App, document.getElementById('app'));