Documentation Index
Fetch the complete documentation index at: https://mintlify.com/cloudflare/partykit/llms.txt
Use this file to discover all available pages before exploring further.
Yjs is a high-performance CRDT library that powers conflict-free collaborative editing — think shared text editors, whiteboards, or any data structure that multiple users edit simultaneously. y-partyserver is the server-side Yjs backend for PartyServer: it extends Server (a Durable Object) to store, synchronize, and persist Yjs documents, while y-partyserver/provider gives clients a drop-in replacement for y-websocket.
Installation
npm install y-partyserver yjs
Minimal server setup
The simplest possible Yjs backend is a single re-export. YServer handles all Yjs sync protocol messages automatically.
// server.ts
export { YServer as MyYServer } from "y-partyserver";
// Then configure wrangler.jsonc and a default fetch handler
// exactly as you would for any PartyServer.
Client setup
Use YProvider from y-partyserver/provider in place of y-websocket. Pass the host, a room name, and a Y.Doc instance.
import YProvider from "y-partyserver/provider";
import * as Y from "yjs";
const yDoc = new Y.Doc();
const provider = new YProvider("localhost:8787", "my-document-name", yDoc);
YProvider options
All constructor options are optional after the first three positional arguments.
import { awarenessProtocol } from "y-protocols";
const provider = new YProvider(
/* host */
"localhost:8787",
/* document/room name */
"my-document-name",
/* Yjs document instance */
yDoc,
{
/* whether to connect to the server immediately */
connect: false,
/* the party server path to connect to, defaults to "main" */
party: "my-party",
/* the path to the Yjs document on the server
* This replaces the default path of /parties/:party/:room.
*/
prefix: "/my/own/path",
/* use your own Yjs awareness instance */
awareness: new awarenessProtocol.Awareness(yDoc),
/* query params to add to the websocket connection
* Can be an object or a function that returns an object
*/
params: async () => ({
token: await getAuthToken()
}),
/* the WebSocket implementation to use (e.g. a polyfill) */
WebSocketPolyfill: WebSocket,
/* interval at which to resync the document (-1 = disabled) */
resyncInterval: -1,
/* maximum time in ms before trying to reconnect (exponential backoff) */
maxBackoffTimeout: 2500,
/* disable cross-tab BroadcastChannel communication */
disableBc: false
}
);
React hook: useYProvider
If you are using React, useYProvider from y-partyserver/react creates and manages the provider lifecycle for you.
import useYProvider from "y-partyserver/react";
import * as Y from "yjs";
const yDoc = new Y.Doc();
function App() {
const provider = useYProvider({
host: "localhost:8787", // optional, defaults to window.location.host
room: "my-document-name",
party: "my-party", // optional, defaults to "main"
doc: yDoc, // optional — hook creates one internally if omitted
options: {
// same options as YProvider constructor
}
});
}
Persistence
By default, YServer keeps the document in memory while at least one client is connected. When all clients disconnect, the in-memory state is lost. Override onLoad and onSave to persist the document to any storage backend (KV, R2, a database, etc.).
// server.ts
import { YServer } from "y-partyserver";
import * as Y from "yjs";
export class MyDocument extends YServer {
/* control how often onSave is called */
static callbackOptions = {
// all of these are optional
debounceWait: /* number, default = */ 2000,
debounceMaxWait: /* number, default = */ 10000,
timeout: /* number, default = */ 5000
};
async onLoad() {
// called once when the first client connects
// load saved bytes and apply them to this.document
const content = (await fetchDataFromExternalService(
this.name
)) as Uint8Array;
if (content) {
Y.applyUpdate(this.document, content);
}
}
async onSave() {
// called periodically after edits, and when the room empties
await sendDataToExternalService(
this.name,
Y.encodeStateAsUpdate(this.document) satisfies Uint8Array
);
}
}
onLoad should initialise this.document from storage. onSave is the right place to write back to a database — it is debounced automatically according to callbackOptions so it won’t fire on every keystroke.
Custom messages
y-partyserver lets you send arbitrary string messages over the same WebSocket connection that carries Yjs sync traffic. This is useful for chat, notifications, or custom RPC alongside collaborative editing.
Sending from the client
// client.ts
import YProvider from "y-partyserver/provider";
import * as Y from "yjs";
const yDoc = new Y.Doc();
const provider = new YProvider("localhost:8787", "my-document-name", yDoc);
// send a custom message to the server
provider.sendMessage(JSON.stringify({ action: "ping", data: "hello" }));
// listen for custom messages from the server
provider.on("custom-message", (message: string) => {
const data = JSON.parse(message);
console.log("Received custom message:", data);
});
Handling on the server
// server.ts
import { YServer } from "y-partyserver";
import type { Connection } from "partyserver";
export class MyDocument extends YServer {
onCustomMessage(connection: Connection, message: string): void {
const data = JSON.parse(message);
if (data.action === "ping") {
// reply to the specific connection
this.sendCustomMessage(
connection,
JSON.stringify({ action: "pong", data: "world" })
);
// or broadcast to everyone
this.broadcastCustomMessage(
JSON.stringify({ action: "notification", data: "Someone pinged!" })
);
}
}
}
Custom message API reference
Client (YProvider):
| Method | Description |
|---|
provider.sendMessage(message: string) | Send a custom string message to the server |
provider.on("custom-message", handler) | Listen for custom messages from the server |
Server (YServer):
| Method | Description |
|---|
onCustomMessage(connection, message) | Override to handle incoming custom messages |
sendCustomMessage(connection, message) | Send a message to a specific connection |
broadcastCustomMessage(message, excludeConnection?) | Broadcast to all connections |
Custom messages are plain strings. JSON is recommended for structured data. They travel on the same WebSocket as Yjs sync, so no additional connection is needed.
Learn more
For editor bindings (Quill, ProseMirror, TipTap, CodeMirror, etc.) refer to the official Yjs documentation. Replace any y-websocket references with y-partyserver/provider.