A mutator called normally is one write per call. For a stream of writes — every keystroke, every
frame of a slider or a drag — call .folded({ key }, args) instead. Each call applies to the
local engine immediately (the view moves every frame), but only the last args per key are
flushed to the server. The plain form stays the committed write your undo/redo replays; the folded
form is the live preview.
Folded writes must be absorbing. Replaying only the last args has to equal replaying all of them — a last-value-wins column patch, never an
increment()-style delta — and a folding mutator can’t read (yield tx.row/tx.query). Break that and the coalesced result diverges from the full stream.
In the wild — Strut
Strut is a slide-deck editor built on Rindle. It streams every keystroke of a slide’s rich-text body through a folded mutation and only writes the plain, committed form on blur. The mutator itself is an ordinary last-write-wins column patch — nothing about it knows it will be folded:
// shared/app-def.ts — one isomorphic body, driven by both tiers
setSlideDoc: shared(
setSlideDocArgs,
function* (tx: IsoTx, a: SetSlideDocArgs): MutationGen {
yield tx.update('slide', { id: a.id, doc: a.doc, modified: a.now })
},
),
shared/app-def.ts L616–622 · Strut
The call site picks the form per event — stream on input, plain on commit. Note the fold key:
a per-keystroke stream keyed by slide.id collapses to one write; a compound `${slide.id}:${idx}`
folds each editable cell independently:
// src/editor/useSlideDocEditor.ts
function writeDoc(doc: string) {
if (idx <= 0) {
mutate.setSlideDoc({ id: slide.id, doc, now: Date.now() })
} else {
const cells = writeCellDoc(slideRef.current.cells, idx, doc)
mutate.setSlideCells({ id: slide.id, cells, now: Date.now() })
}
}
function streamDoc(doc: string) {
if (idx <= 0) {
mutate.setSlideDoc.folded(
{ key: slide.id },
{ id: slide.id, doc, now: Date.now() },
)
} else {
const cells = writeCellDoc(slideRef.current.cells, idx, doc)
mutate.setSlideCells.folded(
{ key: `${slide.id}:${idx}` },
{ id: slide.id, cells, now: Date.now() },
)
}
}
src/editor/useSlideDocEditor.ts L58–82 · Strut
Same mechanism drives Strut’s rotation/scale sliders and its slide-card drag
(mutate.setSlideTransform.folded({ key: s.id }, …)) — any gesture that fires faster than the
network.
See also
- Fine-grained reactivity — folding a per-frame drag while only the dragged row re-renders.
- The browser client — the folded-write API in full.