High-level conceptual overview of the Electric SQL + TanStack DB + Durable Streams stack. Read this BEFORE any planning or coding — it's the map every agent uses to pick the right product for each data-flow problem in the app. Contains concepts only — no class names, no imports, no code. For API details, see the per-product intent skills referenced in each section.
This skill is the single source of truth for what each product in the stack does and when to use it. Every agent — planner, advanced planner, coder, reviewer, QA — reads this file early so they share the same mental model of the stack.
It deliberately contains no code, no class names, no import statements, and no configuration object shapes. Those are details that change between library versions. This file covers what each product is for and when to use it; the detailed per-product skills (listed under "Further reading" in each section) cover how.
When you're about to write a plan or a component, start here. Read the left column, pick the row that matches, use the product in the middle column.
| The problem you're solving | Product | Package |
|---|---|---|
| "What's the current state of entity X, live-synced to the client?" | Electric SQL shapes + |
@electric-sql/client + @tanstack/db + @tanstack/react-db |
| "How do I define my Postgres schema and run migrations?" | Drizzle ORM | drizzle-orm + drizzle-zod + drizzle-kit |
| "How did we get to this state? Append-only log, activity feed, chat history, audit trail, game moves." | Durable Streams client | @durable-streams/client |
| "Ephemeral reactive shared state — presence, cursors, typing indicators, 'who's online'." | StreamDB | @durable-streams/state |
| "Multiple users editing the same rich-text document / whiteboard / code simultaneously, with automatic conflict resolution." | Y-Durable-Streams (Yjs over Durable Streams) | @durable-streams/y-durable-streams |
| "I need to provision an Electric Cloud database or service on demand." | Electric CLI | @electric-sql/cli (via npx) |
| "I need a full-stack React framework with file-based routes and server handlers." | TanStack Start | @tanstack/react-start + @tanstack/start-client-core |
| "I need pre-built accessible UI components styled with the Electric design system." | shadcn/ui + Tailwind CSS | @/components/ui/* + tailwindcss |
If no row matches your problem, you probably don't need Electric at all for that piece — use plain React state, localStorage, or a regular HTTP request.
@electric-sql/clientWhat it is: A real-time sync engine that streams Postgres data to
clients as "shapes". A shape is a filtered SQL query (a table + optional
WHERE clause + columns) that the client subscribes to; the server
keeps the client in sync incrementally via long-polling or SSE.
Use for:
Don't use for:
Mental model: Your database is the source of truth. Clients subscribe to "shapes" (filtered views) and receive live updates as rows change. It's a one-way pipe from server to client — writes go through your own API routes (see TanStack Start below), not directly to Electric.
Composition: Electric shapes are low-level. In practice you wrap them in TanStack DB collections (see next section) for the client-side reactive query interface.
Further reading (API details, when you need them):
node_modules/@electric-sql/client/skills/electric-new-feature/SKILL.mdnode_modules/@electric-sql/client/skills/electric-shapes/SKILL.mdnode_modules/@electric-sql/client/skills/electric-schema-shapes/SKILL.mdnode_modules/@electric-sql/client/skills/electric-proxy-auth/SKILL.mdnode_modules/@electric-sql/client/skills/electric-orm/SKILL.md@tanstack/db + @tanstack/react-dbWhat it is: The client-side reactive database that sits on top of Electric shapes (or other data sources). It materializes a shape into a local collection, lets you run typed queries against it with live-query hooks, and supports optimistic mutations with server-side reconciliation.
Use for:
Don't use for:
Mental model: A client-side reactive database. You define a "collection" (which points at an Electric shape via a proxy route), and then use a live-query hook in your React components to read from it. When the shape updates, your queries automatically re-run and your components re-render. Mutations work optimistically: you call insert / update / delete locally, the framework queues a transaction, and the server reconciles the result back via the shape sync.
⚠️ Important: the live-query hook cannot run server-side during
SSR — it needs a live subscription that only exists on the client.
Any route that calls it needs ssr: false on the route options OR
needs to be wrapped in <ClientOnly>. See the execution-model detail
skill and the create-app SSR handling section.
⚠️ Dates are a dual-path problem (common bug): Data enters a TanStack DB + Electric collection through two independent paths that apply different transforms:
shapeOptions.parser.
The collection's schema is NOT applied here.collection.insert/update): uses the collection's
schema. The parser is NOT involved.For timestamptz / timestamp columns you need BOTH: z.coerce.date()
(or equivalent) in the schema, AND parser: { timestamptz: (v) => new Date(v) } in shapeOptions. Without the parser, sync-delivered
timestamps arrive as ISO strings and any .getTime() /
toLocaleDateString() / formatDistanceToNow() call in the UI
crashes with date.getTime is not a function. This is listed as a
HIGH-severity common mistake in the electric-new-feature intent
skill — see it for the canonical fix.
Further reading:
node_modules/@tanstack/db/skills/db-core/SKILL.mdnode_modules/@tanstack/db/skills/db-core/collection-setup/SKILL.mdnode_modules/@tanstack/db/skills/db-core/live-queries/SKILL.mdnode_modules/@tanstack/db/skills/db-core/mutations-optimistic/SKILL.mddrizzle-orm + drizzle-zod + drizzle-kitWhat it is: A TypeScript-first ORM for Postgres. You define schemas
with strongly-typed column definitions; drizzle-kit generates
migrations from schema diffs; drizzle-zod derives Zod validators
from the schema so server-side code can validate inserts/updates.
Use for:
Don't use for:
Mental model: Schema-first. You define pgTables in one file, the Zod schemas derive from them automatically, and migrations generate from the schema diff. Everything else in the stack (Electric shapes, TanStack DB collections, server routes) takes its type information from the Drizzle schema.
Further reading: There's no bundled intent skill — see the project's own PLAN.md data model conventions + the Drizzle docs website.
@durable-streams/clientWhat it is: An append-only event stream hosted in the cloud. Every event has a monotonically increasing offset; clients can subscribe from any offset and receive events in order. Durable (persistent across server restarts), scalable (horizontally shardable), and works over plain HTTP (long-polling or SSE — no WebSocket infrastructure).
Use for:
Don't use for:
Mental model: A Kafka-like event log. Events are immutable, ordered by offset, and stored durably. Clients tail the stream from an offset and process events in order. Good fit for "history of X" or "log of Y".
Credential note: Durable Streams are NOT auto-provisioned by the
orchestrator. Before the first stream operation, the coder must run
the Electric CLI flow to either provision a stream or accept
user-supplied credentials. See skills/room-messaging/SKILL.md
"Electric CLI — Provisioning External Services".
Further reading:
node_modules/@durable-streams/client/README.md@durable-streams/stateWhat it is: A reactive key-value store materialized on top of a Durable Stream. You define a state schema (a set of collections, each keyed by a primary key), the server persists the stream of changes, and clients materialize the current state into a local in-memory map that stays in sync.
Use for:
Don't use for:
Mental model: Think of it as a small reactive in-memory database
backed by an event stream. Every write appends an event; every client
subscribes and materializes the current state. You get last-write-wins
semantics for free. We actually use StreamDB ourselves inside the
orchestrator (for AgentStore and SecretStore) — that's the canonical
example of the pattern.
Credential note: Same as Durable Streams client — needs the Electric CLI provisioning flow before first use.
Further reading:
node_modules/@durable-streams/state/skills/state-schema/SKILL.mdnode_modules/@durable-streams/state/skills/stream-db/SKILL.md@durable-streams/y-durable-streamsWhat it is: A Yjs CRDT provider that syncs a Y.Doc over a Durable Stream. Yjs is an industry-standard CRDT library for concurrent editing; this package connects it to the Durable Streams transport so you don't need to run a separate WebSocket server for Yjs.
Use for:
Don't use for:
Mental model: The provider is a bridge between a local Y.Doc and a Durable Stream. Local changes are sent upstream; remote changes are applied locally. Conflict resolution happens inside Yjs using CRDT algorithms — you don't need to write any merge logic yourself. Add Yjs awareness for presence (cursors, user names, selections).
DO NOT use @electric-sql/y-electric or plain y-electric — those
are different packages that don't fit our stack.
For API details, TipTap integration patterns, and common pitfalls,
read the intent skills bundled with the package — they're listed in
AGENTS.md and discoverable via npx @tanstack/intent list. Key