bettersync

Getting Started

From zero to working sync in 6 steps

Prefer AI setup?

Copy a prompt for Claude/Cursor that analyzes your project and generates everything.

1. Install

pnpm add bettersync @electric-sql/pglite

2. Create Sync Config

lib/sync.ts
import { betterSync } from 'bettersync'
import { drizzleAdapter } from 'bettersync/adapters/drizzle'
import { db } from './db'
import { projects } from './db/schema'

export const sync = betterSync({
  database: drizzleAdapter(db, { schema: { project: projects } }),
  models: {
    project: {
      fields: {
        id:      { type: 'string', primaryKey: true },
        userId:  { type: 'string' },
        title:   { type: 'string' },
        changed: { type: 'string' },
      },
      scope: (ctx) => ({ userId: ctx.userId }),
    },
  },
  auth: async (req) => {
    const session = await getSession(req.headers)
    if (!session) throw new Error('Unauthorized')
    return { userId: session.user.id }
  },
})

export const syncSchema = sync.schema

3. Mount Handler

app/api/sync/route.ts
import { sync } from '@/lib/sync'
export const POST = sync.handler

Or with Express/NestJS:

import { toNodeHandler } from 'bettersync/node'
app.post('/api/sync', toNodeHandler(sync))

4. Generate Tables

npx @bettersync/cli generate --config lib/sync.ts

5. Create Client

lib/sync-client.ts
import { createSyncClient } from 'bettersync/client'
import { pgliteAdapter } from 'bettersync/adapters/pglite'
import { PGlite } from '@electric-sql/pglite'
import { syncSchema } from './sync'

export const syncClient = createSyncClient({
  database: pgliteAdapter(new PGlite('idb://my-app')),
  schema: syncSchema,
  syncUrl: '/api/sync',
})

6. Use in React

import { SyncProvider, useSync, useSyncQuery, SyncDevtools } from 'bettersync/react'

function App() {
  return (
    <SyncProvider client={syncClient}>
      <TodoList />
      <SyncDevtools />
    </SyncProvider>
  )
}

function TodoList() {
  const sync = useSync()
  const { data: todos } = useSyncQuery(
    (s) => s.model('todo').findMany(), [],
    { live: true },
  )
  return <ul>{todos?.map(t => <li key={String(t.id)}>{String(t.title)}</li>)}</ul>
}

Open two tabs. Add a todo in one. See it in the other.

On this page