Sync that works offline.

A local-first sync engine for TypeScript, React, and Next.js. Every read is instant. Every write works offline. Every client converges.

Instant reads

All reads come from a local IndexedDB replica. No spinners, no round-trips.

Offline support

Mutations queue in a persistent outbox. Changes sync when you reconnect.

Fine-grained reactivity

MobX makes each field observable. Only affected components re-render.

Real-time collaboration

Yjs CRDT integration for multi-user editing of rich text and structured data.

Undo and redo

Transaction-based history tracking, built into the sync client.

Modular

Swap storage, transport, or reactivity adapters. Use only what you need.

Define your models

import { ClientModel, Model, Property } from "@stratasync/core"

@ClientModel("Todo", { loadStrategy: "instant" })
class Todo extends Model {
  @Property() declare title: string
  @Property() declare completed: boolean
}

Create the client

import { createSyncClient } from "@stratasync/client"
import { createMobXReactivity } from "@stratasync/mobx"
import { createIndexedDbStorage } from "@stratasync/storage-idb"
import { GraphQLTransportAdapter } from "@stratasync/transport-graphql"

const client = createSyncClient({
  storage: createIndexedDbStorage(),
  transport: new GraphQLTransportAdapter({
    endpoint: "/api/sync",
    wsEndpoint: "wss://api.example.com/sync/ws",
  }),
  reactivity: createMobXReactivity(),
})

Use React hooks

import { useQuery, useSyncClient } from "@stratasync/react"

function TodoList() {
  const { data: todos } = useQuery("Todo", {
    where: (t) => !t.completed,
  })
  const { client } = useSyncClient()

  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
      <button onClick={() => client.create("Todo", {
        title: "New todo",
        completed: false,
      })}>
        Add
      </button>
    </ul>
  )
}