Skip to main content

Concepts

Understand how Cuttlefish works and what makes it powerful.

Live SQL

When you write useLiveQuery("SELECT * FROM todos"), you’re writing Live SQL - standard SQL queries that automatically update your UI when data changes.
There’s no new query language to learn. If you know SQL, you know how to use Cuttlefish. The queries are live - when the underlying data changes in your database, your component re-renders with the new data automatically.

What’s happening: Live Partial Replica

Behind the scenes, Cuttlefish builds a Live Partial Replica of your database in the browser. When you run a live query:
  1. Cuttlefish Engine (server) executes your SQL against Postgres
  2. Initial data is sent to the browser and stored in RowCache (normalized, in-memory storage)
  3. Subsequent changes stream to the browser in real-time via WebSocket
  4. QueryEvaluator runs your query against the local RowCache
  5. React re-renders with fresh data
Your UI is always in sync because the local replica updates automatically.

Why it’s powerful: Normalized Replication

Here’s what makes Cuttlefish different from other real-time solutions: it sends normalized relations, not denormalized projections.

Example: Messages with user names

Most real-time tools:
Cuttlefish:

Why does this matter?

With denormalized data, if you want to filter messages differently (e.g., by read status), you need a new server query - the data isn’t structured for it. With Cuttlefish’s normalized RowCache, the data is already there. Filter by status, user, date - all instant. No new server queries. This is what enables the Composable Sync pattern.

Unlocking more: Composable Sync

The normalized replica enables a powerful pattern: Composable Sync - “replicate once, query infinitely.”

Two ways to query

Pattern 1: The convenient way
This replicates and queries in one step. Simple and perfect for most cases. Pattern 2: The composable primitives
With the composable pattern, data is already in your local RowCache. Changing filters, sorting, pagination - all instant. No loading states, no server round-trips.

Zero loading states everywhere

Once you’ve replicated data, every interaction is instant:

Sorting

Switching views

The data is already local. Every query is just a different view into the same RowCache.

The pieces: Engine and Client

Cuttlefish is built from two pieces that work together:

Cuttlefish Engine (server)

  • Executes SQL against Postgres
  • Performs normalized replication
  • Streams changes via WebSocket
  • Language-agnostic - use from Python, Go, Ruby, or any language

Client packages (browser)

  • @cuttlefish-sync/core - RowCache, QueryEvaluator, protocol types
  • @cuttlefish-sync/react - React hooks (useLiveQuery, useLocalLiveQuery, replicateQuery)
  • @cuttlefish-sync/kysely - Kysely adapter for type-safe queries
  • @cuttlefish-sync/raw-sql - Raw SQL tagged template adapter
The React client is optimized for the browser, but the core architecture (RowCache + QueryEvaluator) is runtime-agnostic and works in any JavaScript environment.