Skip to content
Onboarding contents

OnboardingFirst steps

Choose a browser client

Compare the standalone wasm store, remote result client, normalized sync, and optimistic app client.

View as Markdown

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.