@colyseus/yjs
v0.18.0
Published
Yjs (CRDT) connector for Colyseus — y-websocket compatible
Downloads
190
Maintainers
Readme
Colyseus × Yjs
@colyseus/yjs — a Yjs (CRDT) connector for Colyseus, plus a live
collaborative-editor demo.
It carries a y-websocket–compatible bridge so any Yjs editor binding (CodeMirror, ProseMirror/TipTap, Quill, Monaco, …) can sync through a Colyseus room — getting Colyseus matchmaking, auth, scaling and reconnection for free, while Yjs handles conflict-free merging and offline edits.
The bundled demo is a shared CodeMirror 6 Markdown editor with live remote cursors.
Layout
. # ← the publishable @colyseus/yjs package (this repo root)
├── src/
│ ├── index.ts # server entry → YjsRoom + protocol
│ ├── protocol.ts # y-websocket wire protocol message types + channel name
│ ├── YjsRoom.ts # SERVER: Colyseus Room ⇄ y-protocol sync + awareness bridge
│ └── client/
│ ├── index.ts # client entry → ColyseusYjsProvider + protocol
│ └── Provider.ts # CLIENT: y-websocket-compatible provider backed by a Room
└── demo/ # private Vite showcase that consumes @colyseus/yjs
├── server/index.ts # registers YjsRoom as the `yjs` room
└── client/main.ts # CodeMirror 6 + y-codemirror.next + the providerThe integration is two small files plus a shared protocol. It relays the
standard y-protocols sync and
awareness frames over Colyseus's binary message channel (ROOM_DATA_BYTES)
instead of a raw WebSocket — so it slots into Colyseus's room lifecycle
(onAuth, onJoin, onLeave, onDispose).
Install
npm install @colyseus/yjsPeer dependencies (both optional, install the side you use): @colyseus/core
0.18.x (server) and @colyseus/sdk 0.18.x (client).
Usage
Server
YjsRoom is a regular Colyseus room — register it like any other, and
override it to add persistence, auth, or server-authoritative edits.
import { defineServer, defineRoom } from 'colyseus';
import { YjsRoom } from '@colyseus/yjs';
export const server = defineServer({
rooms: { yjs: defineRoom(YjsRoom) },
});One Y.Doc is created per room, keyed by options.docName (defaults to the
roomId). To persist the document, assign a YjsPersistence adapter — the
shape matches y-websocket's setPersistence, so y-leveldb / y-redis adapters
port with a thin wrapper:
class PersistentYjsRoom extends YjsRoom {
persistence = new MyLevelDbAdapter(); // bindState() on create, writeState() on dispose
}Server-authoritative edits are just doc.transact(...) — they broadcast to
every client automatically.
Client
ColyseusYjsProvider mirrors y-websocket's event surface (status,
sync/synced, connection-close), so existing editor bindings work
unchanged. Join the room first, then hand the provider the joined Room:
import { Client } from '@colyseus/sdk';
import * as Y from 'yjs';
import { ColyseusYjsProvider } from '@colyseus/yjs/client';
const room = await client.joinOrCreate('yjs', { docName: 'my-doc' });
const doc = new Y.Doc();
const provider = new ColyseusYjsProvider(room, doc);
// ...then bind `doc` / `provider.awareness` to your editor of choice.Running the demo
The demo lives in demo/ and consumes @colyseus/yjs by its public
import paths (aliased to the library source here, so no pre-build is needed).
It links against a sibling Colyseus 0.18 checkout (../colyseus-0.18, see
demo/package.json) — adjust those link: entries, or swap them for the
next (0.18.x) npm dist-tag, to point elsewhere.
cd demo
pnpm install
npm run devOpen the printed URL, then open it again in a second tab (or another device on your LAN). Type in either editor — text merges via CRDT and each peer's caret/selection shows up live via Yjs awareness.
npm run smoke runs a headless two-client check against a running dev server
(text sync + awareness).
Building & publishing
pnpm install # at the repo root — installs the library's own deps
pnpm run build # tsup → build/ (ESM + CJS + .d.ts/.d.cts)CI publishes to npm automatically (see
.github/workflows/publish.yml): on every
push to main it builds and, if package.json#version is not already on
npm, publishes it. So cutting a release is just bumping the version and
merging to main.
One-time setup:
- Add an npm granular-access token
with publish rights to
@colyseus/yjsas the repo secretNPM_TOKEN(Settings → Secrets and variables → Actions). - The workflow attaches npm provenance
via OIDC (
id-token: write) automatically when the repo is public (and its URL matchespackage.json#repository); it's skipped while the repo is private. So no edit is needed if you flip the repo public later.
License
MIT
