Rindle has several browser clients. Choose based on where your data lives and whether the browser predicts writes. React, TanStack, and server rendering are separate integration choices.
All TypeScript clients use the schema, query builder, Store, and view types
from @rindle/client. That package is the shared core. Installing it alone does
not create a database or connect to a server.
Choose by behavior
| You need | Entry point | Browser data | Write behavior |
|---|---|---|---|
| Live queries over data owned by one browser tab | createWasmStore from @rindle/wasm |
In-memory tables and local query results | Applies local row changes immediately |
| Server-maintained results through a custom flat protocol | createRemoteStore from @rindle/remote |
Materialized query results; no local wasm database | Forwards raw writes; no prediction |
| Synced rows with local queries, without optimistic writes | createNormalizedStore from @rindle/normalized plus a NormalizedSource |
A local wasm database populated by retained server queries | Forwards writes through the source; no prediction |
| A full synced app with named mutators | createRindleClient from @rindle/optimistic |
Synced rows, local queries, and pending predictions | Predicts named writes, sends them to the API, then reconciles |
| A custom optimistic transport integration | createOptimisticStore from @rindle/optimistic plus an OptimisticSource |
The same local prediction and rebase engine | You supply the authoritative stream and mutation delivery |
For a browser-only application, start with
Reactive queries in the browser. For the standard Rindle API
server and data tier, start with create-rindle or
manual synced-app setup.
The two middle choices are lower-level compositions. Their transport protocol and lifecycle must match your server. A WebSocket URL alone is not enough to connect every client to every Rindle deployment.
Local engine: no server
createWasmStore(schema) creates an independent in-memory database. Add rows with
store.write, materialize queries, and subscribe to their current results. All
data comes from your application.
It has no built-in network sync, persistence, or optimistic queue. An immediate local write is the final write to this database; there is no server verdict to wait for. See the standalone walkthrough.
Remote results: no local engine or prediction
createRemoteStore(schema, transport) uses the flat result protocol. The
server sends changes to a query result, including nested results. The browser
applies them to an ArrayView; it does not store shared base tables or run wasm.
This can suit a thin client when you control the server protocol. Each remote query needs a registered name and arguments. An arbitrary local builder does not become a server query automatically.
The production rindled WebSocket serves the normalized protocol. It does
not serve flat results to createRemoteStore, even if you provide a lease
resolver. The repository’s private reference server includes a flat server for
integration tests. See custom remote backends
for the boundary and an example.
Normalized sync: local reads without prediction
createNormalizedStore(schema, source) combines the wasm engine with a
NormalizedSource. The source supplies changes to the rows and columns needed
by active server queries. The client shares those rows across subscriptions and
computes local views from them.
For WebSocket input, createRemoteNormalizedSource from @rindle/remote supplies
that source. It understands normalized snapshots and changes. It can subscribe
with an API-issued lease through resolveSubscribe.
This composition does not include the full app connection lifecycle. Your
integration owns lease requests, endpoint selection, affinity tickets, and
rebuilding subscriptions after a reconnect. A returned wsEndpoint does not
move this source’s existing transport. WsTransport reconnects a socket, but
this source does not automatically re-register its queries on that reconnect.
Writes also need an explicit authority. store.write forwards raw mutations
through the source and makes no local prediction. The standard app mutation API
accepts named mutation envelopes, not these raw row operations. Use an application
write endpoint, or implement a compatible sendMutation adapter for your own
server. See normalized composition.
Optimistic app client: the integrated connection
createRindleClient initializes wasm, resolves named query leases through your
API server, and opens the selected data-tier WebSocket. It manages mutation
delivery, reconnects, subscription recovery, and supported endpoint changes.
Call a named mutator to predict a write immediately. Your API server’s HTTP adapter verifies the caller’s credentials, and the API server runs the authoritative mutator. As confirmed changes arrive, the browser reapplies pending mutators to the confirmed rows. This is rebase. Rejected predictions are removed.
The client guide explains construction, reads, writes, and cleanup. You can use this client without React. You can also use it for read-only screens and simply make no mutation calls.
createOptimisticStore exposes the engine composition beneath this constructor.
It accepts an OptimisticSource and a mutator registry. It does not initialize
wasm or construct your app’s HTTP and WebSocket connections for you. Use it when
you are implementing a transport integration; the
backend guide describes its contract.
Understand local completeness
A named query describes what the server must supply. A local query over a normalized or optimistic store reads only the rows currently held in the browser. It opens no subscription of its own.
For example, a named query that retains 50 issues does not download the whole issue table. A local count over that table counts available rows. It cannot establish the server’s total. Releasing server queries can also remove rows that no remaining subscription retains.
Use a named query and its readiness signal when you need an authoritative result. Use a local query when the available rows are sufficient. See preloads and query readiness.
Add UI and storage features separately
@rindle/react wraps a Store; it works with local and remote backends.
TanStack Start adds routing and server integration. Neither choice determines
whether your browser has optimistic writes.
Server rendering uses a separate server store and a browser handoff. Do not construct the live browser client during a render on the server.
None of these constructors automatically persists the synced database or a
pending mutation queue across reloads. The optimistic app client can persist
explicit local-only tables with persistLocal. That option covers UI data such
as drafts, not a durable offline copy of synced data. See
local tables and
persisting local tables.
SQL over HTTP is a separate server-code option. Its database token is trusted, so keep it behind an authorized application endpoint. SQL returns statement results; it does not create a live view or require an optimistic client.