> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cuttlefishsync.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Concepts

> Understand how Cuttlefish works and what makes it powerful

# 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.

```tsx theme={null}
const { data } = useLiveQuery(sql`SELECT * FROM todos WHERE completed = false`)
```

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.

```mermaid theme={null}
graph LR
    A[Cuttlefish Engine<br/>Server] -->|WebSocket| B[RowCache +<br/>QueryEvaluator<br/>Browser]
    B --> C[React UI]
```

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:**

```typescript theme={null}
// Query: SELECT users.name, messages.text FROM messages JOIN users
// Returns denormalized data:
[
  { name: "Alice", text: "Hello" },
  { name: "Alice", text: "How are you?" },
  { name: "Bob", text: "I'm good!" }
]
```

**Cuttlefish:**

```typescript theme={null}
// Same query, but returns normalized relations:
{
  users: [
    { id: 1, name: "Alice" },
    { id: 2, name: "Bob" }
  ],
  messages: [
    { id: 1, user_id: 1, text: "Hello" },
    { id: 2, user_id: 1, text: "How are you?" },
    { id: 3, user_id: 2, text: "I'm good!" }
  ]
}
```

### 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**

```tsx theme={null}
const { data } = useLiveQuery(sql`SELECT * FROM todos WHERE list_id = ${listId}`)
```

This replicates and queries in one step. Simple and perfect for most cases.

**Pattern 2: The composable primitives**

```tsx theme={null}
// Replicate once (on app load or route mount)
replicateQuery(sql`SELECT * FROM todos`)

// Query infinitely - runs locally, zero latency
const { data } = useLocalLiveQuery(sql`SELECT * FROM todos WHERE list_id = ${listId}`)
```

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:

#### Filtering and search

```tsx theme={null}
replicateQuery(sql`SELECT * FROM issues`)

// User toggles filter - instant results from RowCache
const { data } = useLocalLiveQuery(
  sql`SELECT * FROM issues WHERE status = ${filterValue}`
)
```

#### Page navigation

```tsx theme={null}
replicateQuery(sql`SELECT * FROM issues`)

// Navigate to project page - instant, no loading spinner
const { data } = useLocalLiveQuery(
  sql`SELECT * FROM issues WHERE project_id = ${projectId}`
)
```

#### Sorting

```tsx theme={null}
replicateQuery(sql`SELECT * FROM issues`)

// Click column header - instant reorder
const { data } = useLocalLiveQuery(
  sql`SELECT * FROM issues ORDER BY ${sortColumn} ${sortDirection}`
)
```

#### Switching views

```tsx theme={null}
replicateQuery(sql`SELECT * FROM issues`)

// "My Issues" → "Team Issues" → "All Issues" - all instant
const { data: myIssues } = useLocalLiveQuery(
  sql`SELECT * FROM issues WHERE assignee_id = ${userId}`
)
const { data: teamIssues } = useLocalLiveQuery(
  sql`SELECT * FROM issues WHERE team_id = ${teamId}`
)
```

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.
