Local-first collaboration SDK for React, Vue, and Svelte. Batteries-included: Rich text, undo/redo, cursors, and presence.
TypeScript
667
261 commits
updated Sep 20, 2026
True offline-first sync for modern apps—without vendor lock-in
Getting Started • Documentation • Examples • Discussions • Roadmap
Build collaborative apps in hours, not months.
SyncKit is a sync engine that gives you everything for local-first collaboration:
"Add
sync.document()to your app, get real-time sync automatically."
The reality: Building sync from scratch takes months. SyncKit gives you working collaboration in 3 lines of code.
const sync = new SyncKit()
await sync.init()
const doc = sync.document<Todo>('todo-123')
await doc.update({ completed: true })
// ✨ Works offline, syncs automatically, resolves conflicts
LocalWrite - Full-featured collaborative editor showcasing SyncKit's capabilities.
Quick test (30 seconds):
What you'll see:
/h1, /list) with real-time formattingReal-world example:
import { SyncKit } from '@synckit-js/sdk'
import { useSyncDocument } from '@synckit-js/sdk/react'
// Initialize once
const sync = new SyncKit()
await sync.init()
// Use in components
function TaskList() {
const [tasks, { update }] = useSyncDocument<Task[]>('tasks')
const toggleTask = (id: string) => {
update(tasks.map(t =>
t.id === id ? { ...t, completed: !t.completed } : t
))
}
// Syncs across all connected clients automatically
}
True offline-first architecture—not just caching. Your app works perfectly on planes, trains, tunnels, and coffee shops with spotty WiFi.
154KB gzipped - Complete local-first sync solution with everything you need.
What you get:
Size-critical apps? Use Lite variant (46KB gzipped, basic sync only)
Every byte is justified. We chose completeness over minimal size—rich text, undo/redo, cursors, and framework adapters all work together out of the box.
Open source and self-hostable. No vendor lock-in, no surprise $2,000/month bills, complete data sovereignty.
npm install @synckit-js/sdk
import { SyncKit } from '@synckit-js/sdk'
import { SyncProvider, useSyncDocument } from '@synckit-js/sdk/react'
// Initialize (works offline-only, no server needed!)
const sync = new SyncKit()
await sync.init()
function App() {
return (
<SyncProvider synckit={sync}>
<TodoApp />
</SyncProvider>
)
}
function TodoApp() {
const [todo, { update }] = useSyncDocument<Todo>('todo-1')
if (!todo || !todo.text) return <div>Loading...</div>
return (
<div>
<input
type="checkbox"
checked={todo.completed}
onChange={(e) => update({ completed: e.target.checked })}
/>
<span>{todo.text}</span>
</div>
)
}
That's it! Your app now:
Bundle: SyncKit (154KB gzipped) + React (156KB) = ~310KB total
Size-critical? import { SyncKit } from '@synckit-js/sdk/lite' (46KB gzipped, local-only)
graph TD
A[Your Application<br/>React/Vue/Svelte] --> B[SyncKit SDK<br/>TypeScript]
B -->|Simple API| B1[document, text, counter]
B -->|Framework adapters| B2[React/Vue/Svelte hooks]
B -->|Offline queue| B3[Storage adapters]
B --> C[Rust Core Engine<br/>WASM + Native]
C -->|80% of use cases| C1[LWW Sync]
C -->|Collaborative editing| C2[Text CRDTs]
C -->|Advanced features| C3[Custom CRDTs<br/>counters, sets]
C --> D[IndexedDB Storage<br/>Your local source of truth]
D -.->|Optional| E[SyncKit Server<br/>TypeScript/Python/Go/C#]
E -->|Real-time sync| E1[WebSocket]
E -->|Persistence| E2[PostgreSQL/MongoDB]
E -->|Security| E3[JWT auth + RBAC]
style A fill:#e1f5ff,stroke:#333,stroke-width:2px,color:#1a1a1a
style B fill:#fff4e1,stroke:#333,stroke-width:2px,color:#1a1a1a
style C fill:#ffe1e1,stroke:#333,stroke-width:2px,color:#1a1a1a
style D fill:#e1ffe1,stroke:#333,stroke-width:2px,color:#1a1a1a
style E fill:#f0e1ff,stroke:#333,stroke-width:2px,color:#1a1a1a
demo/)Perfect for: Task apps, CRMs, project management, note apps (80% of applications)
import { SyncKit } from '@synckit-js/sdk'
import { useSyncDocument } from '@synckit-js/sdk/react'
// Initialize once
const sync = new SyncKit()
await sync.init()
// Use anywhere
const doc = sync.document<Project>('project-123')
await doc.update({ status: 'completed' })
// Conflicts resolved automatically with Last-Write-Wins
Perfect for: Collaborative editors, documentation, notes
import { useSyncText } from '@synckit-js/sdk/react'
const [text, { insert, delete: del }] = useSyncText('document-456')
await insert(0, 'Hello ')
// Character-level sync, conflict-free convergence
Perfect for: Likes, votes, tags, participants
import { useCounter, useSet } from '@synckit-js/sdk/react'
const [count, { increment, decrement }] = useCounter('likes-789')
await increment() // Conflict-free counter
const [tags, { add, remove }] = useSet<string>('post-tags')
await add('typescript') // Observed-remove set
Different libraries make different trade-offs. Here's how SyncKit compares:
| Feature | SyncKit | Firebase | Supabase | Yjs | Automerge |
|---|---|---|---|---|---|
| Bundle Size (gzipped) | 154KB (46KB lite) | ~150–200KB (typical client) | ~45KB (JS client) | 65KB (core) | 300KB+ (JS/WASM) |
| Text CRDT | ✅ Fugue | ❌ No | ❌ No | ✅ Y.Text | ✅ Yes |
| Rich Text | ✅ Peritext | ❌ No | ❌ No | ⚠️ Limited | ✅ Yes |
| Undo/Redo | ✅ Cross-tab | ❌ No | ❌ No | ⚠️ Basic | ✅ Yes |
| Awareness/Cursors | ✅ Built-in | ❌ No | ❌ No | ⚠️ Extension | ❌ No |
| Framework Adapters | ✅ React/Vue/Svelte | ❌ No | ❌ No | ⚠️ Community | ❌ No |
| True Offline-First | ✅ Native | ⚠️ Limited (cache + persistence) | ❌ No native support | ✅ Full | ✅ Full |
| Works Without Server | ✅ Yes | ❌ No | ❌ No | ✅ Yes | ✅ Yes |
| Self-Hosted | ✅ Yes | ❌ No | ✅ Yes | ✅ Yes | ✅ Yes |
| TypeScript Support | ✅ Native | ✅ Good | ✅ Good | ⚠️ Issues | ✅ Good |
| Production Status | ✅ v0.3.0 | ✅ Mature | ✅ Mature | ✅ Mature | ⚠️ Stable core, evolving ecosystem |
Choose SyncKit if:
Choose alternatives if:
See detailed migration guides →
@synckit-js/sdk - Core SDK (TypeScript) + WASM engine@synckit-js/sdk/react - React hooks and components (export from SDK)@synckit-js/sdk/vue - Vue 3 composables (export from SDK)@synckit-js/sdk/svelte - Svelte 5 stores with runes (export from SDK)@synckit-js/sdk/lite - Lightweight version (local-only, 46KB gzipped)@synckit-js/server - Bun + Hono TypeScript server (stable)Current Version: v0.3.0
What the core sync engine ships today:
An LLM wrote much of the code. The decisions were mine: Fugue and Peritext over the alternatives, the TLA+ models, the API surface, the wire protocol.
Design first, generation under direction, review before ship. None of it asks for trust: the TLA+ specs are in the repo, and the test suites run in CI across five languages.
We welcome contributions from the community!
Ways to contribute:
Need enterprise support?
Contact: danbitengo@gmail.com
SyncKit (lite): 46 KB ████████
Yjs (assembled): 65 KB ███████████
SyncKit (default): 154 KB ████████████████████████████
Firebase: 150 KB ████████████████████████████
Automerge: 300 KB ████████████████████████████████████████████████████████
Local update: <1 ms ████
Cross-tab sync: <1 ms ████
Network sync: 10-50 ms ████████
Firebase (cold): 2-30 s ████████████████████████████████
SyncKit: 3 MB ████
Yjs: 8 MB █████████
Automerge: 180 MB ████████████████████████████████████████
SyncKit's core algorithms are based on published research:
Built with inspiration from:
Special thanks to the local-first community for pioneering this movement.
MIT License - see LICENSE for details.
Copyright (c) 2025 Daniel Bitengo
Built with ❤️ for the local-first future
TypeScript
44.9%
C#
30.6%
Rust
7.7%
JavaScript
5.4%
TLA
3.5%
Python
3.2%
Go
2.9%
Local-first collaboration SDK for React, Vue, and Svelte. Batteries-included: Rich text, undo/redo, cursors, and presence.
TypeScript
667
261 commits
updated Sep 20, 2026
True offline-first sync for modern apps—without vendor lock-in
Getting Started • Documentation • Examples • Discussions • Roadmap
Build collaborative apps in hours, not months.
SyncKit is a sync engine that gives you everything for local-first collaboration:
"Add
sync.document()to your app, get real-time sync automatically."
The reality: Building sync from scratch takes months. SyncKit gives you working collaboration in 3 lines of code.
const sync = new SyncKit()
await sync.init()
const doc = sync.document<Todo>('todo-123')
await doc.update({ completed: true })
// ✨ Works offline, syncs automatically, resolves conflicts
LocalWrite - Full-featured collaborative editor showcasing SyncKit's capabilities.
Quick test (30 seconds):
What you'll see:
/h1, /list) with real-time formattingReal-world example:
import { SyncKit } from '@synckit-js/sdk'
import { useSyncDocument } from '@synckit-js/sdk/react'
// Initialize once
const sync = new SyncKit()
await sync.init()
// Use in components
function TaskList() {
const [tasks, { update }] = useSyncDocument<Task[]>('tasks')
const toggleTask = (id: string) => {
update(tasks.map(t =>
t.id === id ? { ...t, completed: !t.completed } : t
))
}
// Syncs across all connected clients automatically
}
True offline-first architecture—not just caching. Your app works perfectly on planes, trains, tunnels, and coffee shops with spotty WiFi.
154KB gzipped - Complete local-first sync solution with everything you need.
What you get:
Size-critical apps? Use Lite variant (46KB gzipped, basic sync only)
Every byte is justified. We chose completeness over minimal size—rich text, undo/redo, cursors, and framework adapters all work together out of the box.
Open source and self-hostable. No vendor lock-in, no surprise $2,000/month bills, complete data sovereignty.
npm install @synckit-js/sdk
import { SyncKit } from '@synckit-js/sdk'
import { SyncProvider, useSyncDocument } from '@synckit-js/sdk/react'
// Initialize (works offline-only, no server needed!)
const sync = new SyncKit()
await sync.init()
function App() {
return (
<SyncProvider synckit={sync}>
<TodoApp />
</SyncProvider>
)
}
function TodoApp() {
const [todo, { update }] = useSyncDocument<Todo>('todo-1')
if (!todo || !todo.text) return <div>Loading...</div>
return (
<div>
<input
type="checkbox"
checked={todo.completed}
onChange={(e) => update({ completed: e.target.checked })}
/>
<span>{todo.text}</span>
</div>
)
}
That's it! Your app now:
Bundle: SyncKit (154KB gzipped) + React (156KB) = ~310KB total
Size-critical? import { SyncKit } from '@synckit-js/sdk/lite' (46KB gzipped, local-only)
graph TD
A[Your Application<br/>React/Vue/Svelte] --> B[SyncKit SDK<br/>TypeScript]
B -->|Simple API| B1[document, text, counter]
B -->|Framework adapters| B2[React/Vue/Svelte hooks]
B -->|Offline queue| B3[Storage adapters]
B --> C[Rust Core Engine<br/>WASM + Native]
C -->|80% of use cases| C1[LWW Sync]
C -->|Collaborative editing| C2[Text CRDTs]
C -->|Advanced features| C3[Custom CRDTs<br/>counters, sets]
C --> D[IndexedDB Storage<br/>Your local source of truth]
D -.->|Optional| E[SyncKit Server<br/>TypeScript/Python/Go/C#]
E -->|Real-time sync| E1[WebSocket]
E -->|Persistence| E2[PostgreSQL/MongoDB]
E -->|Security| E3[JWT auth + RBAC]
style A fill:#e1f5ff,stroke:#333,stroke-width:2px,color:#1a1a1a
style B fill:#fff4e1,stroke:#333,stroke-width:2px,color:#1a1a1a
style C fill:#ffe1e1,stroke:#333,stroke-width:2px,color:#1a1a1a
style D fill:#e1ffe1,stroke:#333,stroke-width:2px,color:#1a1a1a
style E fill:#f0e1ff,stroke:#333,stroke-width:2px,color:#1a1a1a
demo/)Perfect for: Task apps, CRMs, project management, note apps (80% of applications)
import { SyncKit } from '@synckit-js/sdk'
import { useSyncDocument } from '@synckit-js/sdk/react'
// Initialize once
const sync = new SyncKit()
await sync.init()
// Use anywhere
const doc = sync.document<Project>('project-123')
await doc.update({ status: 'completed' })
// Conflicts resolved automatically with Last-Write-Wins
Perfect for: Collaborative editors, documentation, notes
import { useSyncText } from '@synckit-js/sdk/react'
const [text, { insert, delete: del }] = useSyncText('document-456')
await insert(0, 'Hello ')
// Character-level sync, conflict-free convergence
Perfect for: Likes, votes, tags, participants
import { useCounter, useSet } from '@synckit-js/sdk/react'
const [count, { increment, decrement }] = useCounter('likes-789')
await increment() // Conflict-free counter
const [tags, { add, remove }] = useSet<string>('post-tags')
await add('typescript') // Observed-remove set
Different libraries make different trade-offs. Here's how SyncKit compares:
| Feature | SyncKit | Firebase | Supabase | Yjs | Automerge |
|---|---|---|---|---|---|
| Bundle Size (gzipped) | 154KB (46KB lite) | ~150–200KB (typical client) | ~45KB (JS client) | 65KB (core) | 300KB+ (JS/WASM) |
| Text CRDT | ✅ Fugue | ❌ No | ❌ No | ✅ Y.Text | ✅ Yes |
| Rich Text | ✅ Peritext | ❌ No | ❌ No | ⚠️ Limited | ✅ Yes |
| Undo/Redo | ✅ Cross-tab | ❌ No | ❌ No | ⚠️ Basic | ✅ Yes |
| Awareness/Cursors | ✅ Built-in | ❌ No | ❌ No | ⚠️ Extension | ❌ No |
| Framework Adapters | ✅ React/Vue/Svelte | ❌ No | ❌ No | ⚠️ Community | ❌ No |
| True Offline-First | ✅ Native | ⚠️ Limited (cache + persistence) | ❌ No native support | ✅ Full | ✅ Full |
| Works Without Server | ✅ Yes | ❌ No | ❌ No | ✅ Yes | ✅ Yes |
| Self-Hosted | ✅ Yes | ❌ No | ✅ Yes | ✅ Yes | ✅ Yes |
| TypeScript Support | ✅ Native | ✅ Good | ✅ Good | ⚠️ Issues | ✅ Good |
| Production Status | ✅ v0.3.0 | ✅ Mature | ✅ Mature | ✅ Mature | ⚠️ Stable core, evolving ecosystem |
Choose SyncKit if:
Choose alternatives if:
See detailed migration guides →
@synckit-js/sdk - Core SDK (TypeScript) + WASM engine@synckit-js/sdk/react - React hooks and components (export from SDK)@synckit-js/sdk/vue - Vue 3 composables (export from SDK)@synckit-js/sdk/svelte - Svelte 5 stores with runes (export from SDK)@synckit-js/sdk/lite - Lightweight version (local-only, 46KB gzipped)@synckit-js/server - Bun + Hono TypeScript server (stable)Current Version: v0.3.0
What the core sync engine ships today:
An LLM wrote much of the code. The decisions were mine: Fugue and Peritext over the alternatives, the TLA+ models, the API surface, the wire protocol.
Design first, generation under direction, review before ship. None of it asks for trust: the TLA+ specs are in the repo, and the test suites run in CI across five languages.
We welcome contributions from the community!
Ways to contribute:
Need enterprise support?
Contact: danbitengo@gmail.com
SyncKit (lite): 46 KB ████████
Yjs (assembled): 65 KB ███████████
SyncKit (default): 154 KB ████████████████████████████
Firebase: 150 KB ████████████████████████████
Automerge: 300 KB ████████████████████████████████████████████████████████
Local update: <1 ms ████
Cross-tab sync: <1 ms ████
Network sync: 10-50 ms ████████
Firebase (cold): 2-30 s ████████████████████████████████
SyncKit: 3 MB ████
Yjs: 8 MB █████████
Automerge: 180 MB ████████████████████████████████████████
SyncKit's core algorithms are based on published research:
Built with inspiration from:
Special thanks to the local-first community for pioneering this movement.
MIT License - see LICENSE for details.
Copyright (c) 2025 Daniel Bitengo
Built with ❤️ for the local-first future
TypeScript
44.9%
C#
30.6%
Rust
7.7%
JavaScript
5.4%
TLA
3.5%
Python
3.2%
Go
2.9%