Reactive real-time collaboration primitives for Svelte โ powered by Y.js CRDTs
Transform any Svelte app into a collaborative experience with just a few lines
of code. Built on top of battle-tested Y.js CRDTs, svelte-collab provides
seamless real-time synchronization with automatic conflict resolution.
Real-time collaboration in action - changes sync instantly across multiple browser tabs!
๐น Watch Full Demo Video - Higher quality WebM format
$ reactivitynpm install svelte-collab
# or
pnpm add svelte-collab
# or
yarn add svelte-collab
npm install
# or
pnpm install
# or
yarn install
npm run server
The server will start at ws://localhost:1234.
<script lang="ts">
import { collabWritable } from 'svelte-collab';
import { onDestroy } from 'svelte';
const store = collabWritable(
{ count: 0, message: 'Hello!' },
{
room: 'my-room',
serverUrl: 'ws://localhost:1234',
persist: true
}
);
// Clean up on component destroy
onDestroy(() => {
store.destroy();
});
</script>
<!-- Use like a normal Svelte store -->
<div>
<p>Count: {$store.count}</p>
<button onclick={() => store.update(s => ({ ...s, count: s.count + 1 }))}>
Increment
</button>
<input bind:value={$store.message} />
</div>
Open the same page in multiple browser tabs and watch changes sync in real-time! โจ
collabWritable(initialValue, options)Creates a collaborative Svelte store.
initialValue T - Initial store value (must be an object)options CollabOptions:room string (required) - Unique room identifierserverUrl string (optional) - WebSocket server URLpersist boolean (default: true) - Enable IndexedDB persistenceuser UserInfo (optional) - User information for presencestateName string (default: 'state') - Y.Map namedebug boolean (default: false) - Enable debug loggingA CollabStore<T> with the following methods:
subscribe(callback) - Subscribe to store changes (standard Svelte store)set(value) - Set the entire store valueupdate(updater) - Update store with a functiongetDoc() - Get underlying Y.DocgetYMap() - Get underlying Y.MapgetConnectionState() - Get current connection stateconnect() - Manually connect to serverdisconnect() - Manually disconnect from serverdestroy() - Clean up and destroy the store<script lang="ts">
import { collabWritable } from 'svelte-collab';
const counter = collabWritable({ count: 0 }, { room: 'counter-room' });
</script>
<button onclick={() => counter.update(s => ({ count: s.count + 1 }))}>
Count: {$counter.count}
</button>
<script lang="ts">
import { collabWritable } from 'svelte-collab';
const todos = collabWritable(
{ items: [] as string[] },
{ room: 'todos', serverUrl: 'ws://localhost:1234' }
);
let newItem = $state('');
function addTodo() {
if (!newItem.trim()) return;
todos.update(s => ({
items: [...s.items, newItem.trim()]
}));
newItem = '';
}
</script>
<input bind:value={newItem} onkeydown={(e) => e.key === 'Enter' && addTodo()} />
<button onclick={addTodo}>Add</button>
<ul>
{#each $todos.items as item}
<li>{item}</li>
{/each}
</ul>
<script lang="ts">
import { collabWritable } from 'svelte-collab';
const store = collabWritable({ data: 'test' }, {
room: 'status-demo',
serverUrl: 'ws://localhost:1234'
});
let status = $derived(store.getConnectionState());
</script>
<div class="status status-{status.status}">
{status.status}
</div>
The package includes a simple WebSocket server for development:
npm run server
Or run it directly:
npx tsx server/websocket.ts
PORT - Server port (default: 1234)HOST - Server host (default: localhost)ws://localhost:1234 - Y.js WebSocket connectionGET / - Server status pageGET /health - Health check endpointGET /rooms - List active roomsFor production, you can:
server/websocket.ts to your hosting
platformnpx y-websockety-websocket packageExample Deploy to Railway/Render:
FROM node:20-alpine
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install
COPY server ./server
RUN pnpm add -g tsx
CMD ["tsx", "server/websocket.ts"]
# Run all tests
npm test
# Run tests in watch mode
npm run test:unit
# Run type checking
npm run check
This project uses Biome for fast linting and formatting:
# Check code quality (linting + formatting)
npm run biome:check
# Auto-fix issues
npm run biome
# Format only
npm run format
# Lint only
npm run lint
svelte-collab wraps Y.js CRDTs in a Svelte-friendly API:
โโโโโโโโโโโโโโโโโโโ
โ Svelte Store โ โ Your app uses this
โโโโโโโโโโฌโโโโโโโโโ
โ
โโโโโโผโโโโโโ
โ Y.Map โ โ CRDT magic happens here
โโโโโโฌโโโโโโ
โ
โโโโโโผโโโโโโโโโโโโโโโโโ
โ Providers โ
โ โข WebSocket โ โ Sync across clients
โ โข IndexedDB โ โ Local persistence
โโโโโโโโโโโโโโโโโโโโโโโ
import * as Y from "yjs";
import { collabWritable } from "svelte-collab";
const ydoc = new Y.Doc();
const store = collabWritable({ data: "test" }, {
room: "custom",
ydoc, // Use your own Y.Doc
});
// Works offline with just local persistence
const store = collabWritable({ data: "test" }, {
room: "offline-room",
persist: true,
// No serverUrl = offline mode
});
const store = collabWritable({ data: "test" }, {
room: "manual",
serverUrl: "ws://localhost:1234",
});
// Disconnect
store.disconnect();
// Reconnect later
store.connect();
Current Version: 0.0.1 (MVP)
collabWritable storeContributions are welcome! Please see PROJECT_SPEC.md for development phases and planned features.
MIT ยฉ rajsibajsi
Made with โค๏ธ for the Svelte community