Skip to main content

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):
MethodDescription
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):
MethodDescription
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.

Build docs developers (and LLMs) love