tracks02 / 04◐ ui — for interfaces
This is the track everything else grew out of, and it comes in stages. A reactive database in your browser makes UI updates simple. One in your backend makes collaboration simple. Run it on both ends and you have the full local-first stack — instant reads, optimistic writes, and sync you don't build. Start at whichever stage your app is at.
01 · reactive db in your browser
The engine ships as wasm — ~200 kB gzipped — and runs in-process over a local database. You write a query once and materialize() it; the subscription fires only when the result actually changes, with the exact delta already applied. No cache layer, no invalidation, no state-library choreography — the query is the state.
02 · reactive db in your backend
The rindled daemon holds the data and every live query; your API server stays a stateless authority that resolves named queries and runs the authoritative mutators. Every commit — whoever made it — fans out to every subscriber as ordered row deltas. Two users in the same view see each other's edits because that's what the engine does, not because you built broadcast plumbing.
03 · reactive db on both ends
Give the client its own engine over its own local database, fed by the server's delta stream. Reads resolve locally and instantly. Writes are predicted mutators: they apply synchronously, the engine rebases them as the server confirms, and a rejection snaps back on its own — you never write rollback code. Same query API as stage one; the client just computes its own result now. (This same rebase is how the ai track lets agents branch reality.)
04 · the details
Route loaders preload the same named queries the page renders, dehydrate the rows, and hand off to the live client — no flash, no second rendering system.
docs →fragmentsone root query per pageCompose data the way you compose components: descendant fragments roll up into a single composed query, so a page ships one subscription, not thirty.
docs →local-only tablesdrafts, selections, prefsUI state lives in the same reactive store and composes with synced rows in one query — it just never crosses the wire, and never rebases.
docs →mutation foldingdrags become one writemutate.setScore.folded(…) collapses a run of same-key calls — a 200-event drag — into one queued mutation, while views track every intermediate frame.
docs →Cursors, presence, and room state on the same subscription plane your queries already use — no second websocket stack.
05 · ship it
The three stages are one docs path, walked in order: the quickstart stands up all three tiers, then the client, fragments, SSR, and the API server go deep on each part. Scaffold it with create-rindle or wire it by hand.