---
title: "Sync & Conflict Resolution"
description: Configure automatic sync strategies and conflict resolution for Persist stores.
section: Persist
order: 3
updatedAt: 2026-05-12
slug: persist/sync
---
# Sync & Conflict Resolution

## How sync works

Persist is offline-first. Writes go to the local store immediately and never block on network. A background sync queue pushes changes to the remote and pulls remote changes back.

```
local write --> local store --> sync queue --> remote
                                   ^
                                   |
                         pull remote changes
```

## Configuration

```ts
import { createStore } from "@cuitty/persist";

const store = await createStore({
  name: "my-app",
  adapter: "sqlite",
  path: "./data/my-app.db",
  sync: {
    remote: "postgres",
    strategy: "last-write-wins",
    interval: 5000, // sync every 5 seconds (default: 10000)
  },
});
```

### Options

| Option       | Type     | Default            | Description                         |
| ------------ | -------- | ------------------ | ----------------------------------- |
| `remote`     | `string` | --                 | Remote adapter type or connection   |
| `strategy`   | `string` | `"last-write-wins"`| Conflict resolution strategy        |
| `interval`   | `number` | `10000`            | Sync interval in milliseconds       |

## Strategies

### Last-write-wins (LWW)

The simplest strategy. Each record carries a timestamp; the most recent write wins on conflict.

```ts
sync: {
  remote: "postgres",
  strategy: "last-write-wins",
}
```

Good for settings, preferences, and data where overwrites are acceptable.

### CRDTs

Conflict-free replicated data types that merge automatically without data loss. Best for collaborative data.

```ts
sync: {
  remote: "postgres",
  strategy: "crdt",
}
```

Persist uses operation-based CRDTs under the hood. Counters, sets, and maps merge deterministically across peers.

### Custom merge

Supply a function that receives both versions and returns the resolved value.

```ts
sync: {
  remote: "postgres",
  strategy: "custom",
  merge(local, remote) {
    // Example: keep the record with more fields
    const localKeys = Object.keys(local.value);
    const remoteKeys = Object.keys(remote.value);
    return localKeys.length >= remoteKeys.length ? local : remote;
  },
}
```

## Conflict resolution callbacks

Register a callback to handle conflicts interactively or log them.

```ts
store.events.on("conflict", (event) => {
  console.log(`Conflict on key: ${event.key}`);
  console.log("Local:", event.local);
  console.log("Remote:", event.remote);
  console.log("Resolved:", event.resolved);
});
```

## Offline-first behavior

When the device is offline, Persist continues to accept reads and writes against the local store. Changes accumulate in the sync queue and flush automatically when connectivity returns.

The sync queue is persisted to disk, so pending changes survive app restarts.

## Monitoring sync status

```ts
const status = store.sync.status();
// { state: "synced" | "syncing" | "offline", pending: number, lastSync: Date }

store.events.on("sync", (event) => {
  console.log(`Synced ${event.pushed} up, ${event.pulled} down`);
});
```