Skip to main content

Quick Start

We’ll get Cuttlefish running in a Next.js app in 5 minutes.
1

Prerequisites

Before we begin, we’ll need:Node.js 20+ - Required for running the Cuttlefish dev server and our Next.js app.A Next.js app - Don’t have one? Create a new Next.js app:
npx create-next-app@latest
A local Postgres database - Don’t have one? Run Postgres with Docker:
docker run --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=postgres -d postgres
A database table to query - Starting fresh? Create a sample todos table:
docker exec -i postgres psql -U postgres -d postgres -c "CREATE TABLE todos (id SERIAL PRIMARY KEY, title TEXT NOT NULL, completed BOOLEAN DEFAULT FALSE, created_at TIMESTAMPTZ DEFAULT NOW());"
2

Installation

Install Cuttlefish packages:
npm install @cuttlefish-sync/dev-server @cuttlefish-sync/core @cuttlefish-sync/react @cuttlefish-sync/raw-sql
3

Setup

We’ll wrap our app with CuttlefishProvider in app/layout.tsx:
import { CuttlefishProvider } from '@cuttlefish-sync/react'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <CuttlefishProvider serverUrl="ws://localhost:4000/socket">
          {children}
        </CuttlefishProvider>
      </body>
    </html>
  )
}
4

Start the dev server

In a separate terminal, start the Cuttlefish dev server:
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres npx cuttlefish-dev-server start
We should see: Cuttlefish Engine running on http://localhost:4000
5

Your first live query

The useLiveQuery hook executes a SQL query and automatically updates our component when data changes in the database. It returns data, loading, and error states just like any async hook.Create app/page.tsx:
'use client'

import { sql } from '@cuttlefish-sync/raw-sql'
import { useLiveQuery } from '@cuttlefish-sync/react'

export default function Home() {
  const { data, loading, error } = useLiveQuery(
    sql`SELECT * FROM todos ORDER BY created_at DESC`
  )

  if (loading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>

  return (
    <div>
      <h1>My Todos</h1>
      <ul>
        {data?.map((todo) => (
          <li key={todo.id}>
            {todo.title} {todo.completed ? '✓' : ''}
          </li>
        ))}
      </ul>
    </div>
  )
}
6

Verify it works

Let’s open our Next.js app in the browser at http://localhost:3000. We should see “My Todos” with our existing todos (or an empty list).Now let’s see live updates in action. Open a new terminal and insert a todo directly into the database:
docker exec -i postgres psql -U postgres -d postgres -c "INSERT INTO todos (title, completed) VALUES ('Buy groceries', false);"
Watch the browser - the new todo appears instantly, with no page refresh.This is Cuttlefish’s core value: our UI stays synchronized with the database in real-time. Any change to the database - whether from another user, a background job, or a manual SQL command - appears immediately in all connected clients.

Next steps

Congratulations! We’ve built our first live-updating React app with Cuttlefish. Learn the concepts: Explore advanced patterns: API Reference: