Build an app with live messages, optimistic writes, and a shared database.
create-rindle generates the browser client, API server, and local data tier in a
TanStack Start project.
This guide starts the app on your computer. You will see a message sync between two browser windows, then change the generated project.
Use this starter when you want a new synced TypeScript app with routing and server
rendering included. To add sync to another framework, use the
manual quickstart. For local queries without a server,
start with the browser engine. For remote reads or normalized
sync without prediction, compare the browser clients.
For an embedded Rust database, use rindle-replica.
Before you start
You need Node.js 22.18 or later and npm or pnpm. The starter uses TypeScript and React. It includes the local Rindle binaries and requires no cloud account.
1. Create the project
With pnpm, run:
pnpm create rindle my-app
cd my-app
pnpm dev
With npm, run:
npm create rindle@latest my-app
cd my-app
npm run dev
The generator installs dependencies with your package manager. The development command starts the app at localhost:3000.
2. See live updates
- Open localhost:3000 in two browser windows.
- Create a room in the first window.
- Open the room in both windows.
- Post a message in the first window.
The message appears immediately in the first window and syncs to the second. The room’s message count updates with each message.
To see a rejected write, create a room named spam. The room appears briefly,
then disappears when the API server rejects it. A toast explains the rejection.
The starter includes a development identity so you can write immediately. Before production, replace it with your own authentication provider.
3. Understand the development command
The generated dev script runs one lifecycle command:
rindle dev --migrate --gen shared/schema.gen.ts -- vite dev --port 3000
rindle dev reads rindle.ncl and starts the local write master, follower, and
fleet edge. The master accepts database writes. The follower serves live queries,
and the edge routes requests to them. The command applies migrations and generates
shared/schema.gen.ts. Then it starts TanStack Start with RINDLE_URL and
RINDLE_DATABASE_TOKEN.
The database token stays on the server. The browser calls the app’s
/api/rindle/* routes. An authorized subscription response, called a lease,
gives it the endpoint for live updates.
What it generates
The starter generates the same three tiers as the manual quickstart:
| Tier | In the template | What it does |
|---|---|---|
| Browser | src/rindle-client.ts, src/rindle-tanstack.ts, src/components/*.queries.ts, src/routes/* |
Displays live query results and predicts writes immediately |
| API authority | server/app-api.ts, server/rindle-http.ts, src/routes/api.rindle.*.tsx |
Checks access, resolves named queries, and runs authoritative mutators |
| Data tier | rindle.ncl, migrations/*.sql |
Stores data, applies migrations, and sends live query changes |
The schema is SQL-first. Edit or add migrations/*.sql. The dev loop applies the
migration to the write master and regenerates shared/schema.gen.ts from the follower’s
introspected schema. A file can be pure DDL (including reviewed destructive drops) or
pure DML for seeds and bounded backfills. Keep the two kinds in separate ordered files.
Keep relationships, normalization, and mutators in shared/app-def.ts.
Why TanStack Start
The starter uses TanStack Start because it gives the template one home for the browser, SSR, and server routes:
src/routes/api.rindle.query.tsx,api.rindle.read.tsx, andapi.rindle.mutate.tsxare the browser-facing API routes.- SSR reads call the same app authority in-process, so first paint and client subscriptions use the same query registry.
@rindle/tanstacklets each file route declare its query once, then owns server preloading, client navigation readiness, cancellation, and the seed-to-live provider handoff.- TanStack Router’s file routes keep app screens and Rindle query modules close without forcing the Rindle APIs themselves to depend on TanStack.
This template chooses the optimistic client and a replicated data tier. Other Rindle integrations do not require that combination. TanStack Start is the template’s application framework; the query engine and client APIs also work outside it.
Where to look first
| File | Why it matters |
|---|---|
migrations/0001_init.sql |
The source-of-truth SQL schema |
shared/schema.gen.ts |
Generated @rindle/client schema; do not hand-edit |
shared/app-def.ts |
Schema re-export, relationships, normalization, and predicted mutators |
src/components/*.queries.ts |
Named root queries and fragments, co-located with UI |
src/rindle-client.ts |
The one browser client setup call |
src/rindle-tanstack.ts |
The shared route-loader/provider integration |
src/ssr.ts |
Server-only first-paint preload helper |
src/devtools.tsx |
Dev-only Rindle devtools panel mount |
server/app-api.ts |
Authoritative query registry, SQL mutators, policy |
rindle.ncl |
The topology rindle dev (local) and rindle deploy (cloud) both read — followers = 1, the colocated pair; loopback in dev |
AGENTS.md |
App instructions for coding agents, including generated files, mutators, and named queries |
Customize it
Treat the generated app as a working baseline:
- Change tables in
migrations/*.sql, then letpnpm devregenerate the typed schema. - Add relationships and mutators in
shared/app-def.ts. - Add named root queries or fragments beside the components that read them.
- Replace the demo auth seam in
shared/auth.tsand the policy inserver/app-api.ts. - If you point the app at Rindle Cloud or a self-hosted fleet, keep
RINDLE_DATABASE_TOKENserver-only.
The same rindle.ncl drives the cloud:
- Run
rindle login. - Run
pnpm rindle:deploy. It provisions (or re-attaches) the Sync plan — a managed master + follower on Rindle’s packed OVH fleet — and records the binding in.rindle/cloud.json. Commit that file. - Run
pnpm rindle:migrate:cloudto pushmigrations/*.sqlthrough the Cloud proxy.
Local dev reads the same authored topology through rindle dev. See
deploy.
For the underlying commands, see @rindle/cli. For the
manual version of the same app shape, keep the quickstart
open beside the generated project.
Next steps
- Synced-app quickstart - the same architecture, built by hand.
- The three-tier architecture - why the browser, API authority, and daemon are separated.
@rindle/cli- therindletoolchain the template uses for the daemon, migrations, and schema generation.- The browser client -
createRindleClient, optimistic mutators, and live reads. - Server rendering - the first-paint preload and live-store handoff.
- Devtools - the dev-only mutation timeline, query inspector, and delta stream.