Open-source project
yjs/y-websocket avatar
yjs/y-websocket

y-websocket: one WebSocket endpoint for synced Yjs documents

Websocket Connector for Yjs

714 stars314 forksJavaScriptMIT

At a glance

What is it?
y-websocket is the reference WebSocket provider for Yjs, pairing a client that syncs document updates and awareness with a simple in-memory server backend that does not scale. It suits small deployments and teams that want existing cookie or header authentication reused over a WebSocket.
Who is it for?
y-websocket is a good fit for a single room application that already carries its identity in cookies or headers and can tolerate a non-scaling server, and a poor fit for a multi-tenant product that needs per-document authorisation and horizontal scale. Before adopting it, run the documented server on port 1234, connect a client, and check whether the 4400 to 4499 close code convention matches the rejection behaviour your own server or proxy produces.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 79 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A single endpoint, one room per document

y-websocket implements what the README calls a classical client server model. Clients connect to a single endpoint over WebSocket, and the server distributes two things: document updates, which is the actual Yjs shared type traffic, and awareness information, the lightweight per-client state that carries cursor positions and presence. The client side is the WebsocketProvider, created with a server URL, a room name and a Y.Doc, and from that moment the document is kept in sync with every other client in the same room.

This is aimed squarely at people building collaborative editing on top of Yjs, and the choice of transport is the interesting part. WebSocket is a persistent, bidirectional channel, so a client that types sends a small update rather than polling for a full document, and the provider exposes flags such as wsconnected, wsconnecting, shouldConnect and synced so an interface can show connection state without reaching into the socket. The provider assumes the Yjs document outlives the connection. The documentation states that as long as the provider or the connected document is not destroyed, changes are synced to other clients through the connected server.

The bundled backend is in-memory and does not scale

The repository contains what the README describes as a simple in-memory backend that can persist to databases, but it cannot be scaled easily. That single sentence defines the ceiling of this project. The included server is a development and small deployment tool, not a production cluster, and the README says so plainly rather than implying that a single process can carry a busy application.

The alternative is to swap the backend. The README lists several y-websocket compatible backends: @y/websocket-server, hocuspocus, y-sweet, @y/hub, ypy-websocket, pycrdt-websocket and yrs-warp. The package that was previously bundled inside y-websocket now lives in its own forkable repository, @y/websocket-server, and the README recommends it as the fastest way to get started. @y/hub is described as the scalable alternative that provides auth and can persist to different backends. This is the real decision an adopter makes: keep the provider and choose a server, or write one. The provider code, including the close code handling and cross-tab behaviour, is the reusable half of the project in either case.

Identity travels in query parameters, cookies and headers

The README makes a specific argument for using a WebSocket server as the central point: websockets send header information and cookies, so existing authentication mechanisms can be reused with this server, and the WebSocket provider is a solid choice when you want a central source that handles authentication and authorization. Nothing in the provider implements an auth scheme itself. The extension point is the params object, which becomes a query string on the server URL, so a bearer style token can be passed as a parameter.

Two properties of that mechanism matter in production. First, the parameters are attached to the URL, which means they travel through connection logs, proxies and browser history far more readily than a header would. Second, the documentation states that wsProvider.params can be safely updated and that the new values are used when a new connection is established, and that if it contains an auth token it should be updated regularly. That is a token refresh contract built into the provider, and it works only if the server actually closes the old connection or the client reconnects, since a live socket keeps the parameters it was opened with. The provider does not offer per-document authorisation, so a server that lets one client into a room can expose the whole room to it.

Close codes in the 4400 range decide whether a client retries

Reconnection is not a single boolean, and y-websocket makes the distinction explicit. The default shouldReconnect callback treats close codes from 4400 to 4499 as permanent, meaning the provider stops reconnecting and fires the closed event instead. The README notes that this is never called when the connection is closed by the client itself, for example through disconnect. The point of the range is to let a server say no without the client hammering it forever.

The remaining knobs are simple. connect defaults to true and can be set to false when you want to open the socket yourself with connect(). maxBackoffTime defaults to 2500 and caps the exponential backoff between retries. The provider also exposes syncStatus, an object holding connected, receivedInitialSync, localUpdatesSynced, localUpdatesAge, lastMessageAge and a status of green, yellow or red, though the documentation marks this as working only with certain backends such as yhub. The interesting part is the custom range: a non standard close code has to travel through your reverse proxy and framework unmangled, and a proxy that rewrites close codes will quietly turn a permanent rejection into an endless retry loop. That is the first thing to test when a client reconnects against a server that is refusing it.

Two tabs in one browser never reach the server

The provider supports cross-tab communication, so opening the same document in the same browser exchanges changes without a server round trip. The mechanism is the Broadcast Channel API, with localStorage as a fallback, and the bcconnected flag reports whether the instance is currently talking to other browser windows that way.

This is an efficiency measure, not a security boundary. It applies only to windows in the same browser profile, which is why the fallback to localStorage matters: the API is not available in every context, and the provider needs a second path to keep two tabs in step. Nothing here helps a user on another machine or another browser, and a second device still depends entirely on the WebSocket server for both updates and awareness. The trade-off is that local state can move between tabs faster than a server can round trip, which is helpful for a cursor that should not lag, and it also means a tab that is offline from the server can still apply a local edit, leaving the reconciliation to the CRDT merge when the connection returns.

Running a first server and a first client

The stable package and the unstable one are installed under different names, and the README marks the stable release as the recommended option. The development line is published separately, and the README is explicit that most users should stay on the stable y-websocket package with Yjs v13 for now, while the main branch carries the unstable @y/websocket release that adds support for Yjs v14.

sh
# stable release (recommended)
npm i y-websocket
sh
# main branch / unstable release
npm i @y/websocket

The server is a separate package now. Installing it and starting it on a fixed host and port is the whole setup, and the port 1234 is the one the client example and the repository Dockerfile both use.

sh
npm install @y/websocket-server
HOST=localhost PORT=1234 npx y-websocket

On the client, the provider takes the server URL, a room name and the document, and the status event reports connection state so you can log it during a first run.

js
import * as Y from '@y/y'
import { WebsocketProvider } from 'y-websocket'
const doc = new Y.Doc()
const wsProvider = new WebsocketProvider('ws://localhost:1234', 'my-roomname', doc)
wsProvider.on('status', event => {
  console.log(event.status) // logs "connected" or "disconnected"
})

In Node.js there is no built in WebSocket, so the provider takes a polyfill through the wsOpts object.

js
const wsProvider = new WebsocketProvider('ws://localhost:1234', 'my-roomname', doc, { WebSocketPolyfill: require('ws') })

The package metadata and the release list describe two lines at once

The repository sits between a stable and a development line, and the files show both. The package.json in the repository is named @y/websocket at version 4.0.0-rc.4, uses ESM with a CommonJS build in dist, and pulls in @y/protocols and lib0, with @y/y declared as a peer dependency. The published stable release is v3.1.0, released on 2026-08-06, alongside v3.1.0-rc.5 and v3.1.0-rc.4 in June 2026, and the last push was on 2026-08-06. The repository is not archived.

The scripts in package.json describe the development workflow: a test entry point at node ./test/index.js, a lint step running standard together with tsc, and a dist step that cleans, bundles with rollup and runs the TypeScript compiler. Those commands are what a contributor would use, and the presence of a Dockerfile that exposes 1234 and starts the server shows the intended container shape for the bundled backend.

The licence is MIT, which is permissive enough to use the provider inside a commercial product, and the practical upgrade cost is the usual one for a library with a stable public API: a version bump on the client side, plus attention to the close code and syncStatus behaviour if the server side changes. The documentation explicitly states that it primarily documents the currently stable y-websocket release and its usage with yjs v13, so the v14 work is opt in rather than something an adopter has to absorb.

Editorial conclusion

y-websocket is a good fit for a single room application that already carries its identity in cookies or headers and can tolerate a non-scaling server, and a poor fit for a multi-tenant product that needs per-document authorisation and horizontal scale. Before adopting it, run the documented server on port 1234, connect a client, and check whether the 4400 to 4499 close code convention matches the rejection behaviour your own server or proxy produces.

Frequently asked questions

How do I install y-websocket and start a server?

Install the stable package with npm i y-websocket, then install the separate server package and run it with HOST=localhost PORT=1234 npx y-websocket. The server package was previously bundled inside y-websocket and now lives in its own repository.

How does y-websocket handle authentication?

WebSockets send headers and cookies, so existing authentication mechanisms can be reused with the server, and the provider can attach query parameters to the server URL. The provider does not implement authorisation itself, that belongs to the backend.

Why does my y-websocket client stop reconnecting after a disconnect?

By default the provider treats close codes between 4400 and 4499 as permanent and stops reconnecting, firing the closed event. The behaviour is not applied when the client closes the connection itself through disconnect.

Does y-websocket work in Node.js?

The provider needs a WebSocket object, so in Node.js you pass a polyfill through the wsOpts option using the ws package. The README gives the example with the WebSocketPolyfill option.

Should I use y-websocket or @y/websocket?

The README recommends the stable y-websocket package with Yjs v13, and states that the main branch carries the unstable @y/websocket release which adds support for Yjs v14. Most users should stay on the stable line for now.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. yjs/y-websocket on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/yjs-y-websocket.svg)](https://hysenlabs.com/projects/yjs-y-websocket)

Was this useful?