npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

lexxy-realtime

v0.2.1

Published

Real-time collaborative editing for Lexxy (Lexical) over Yjs — works with any Yjs provider; reliable yrby over ActionCable/AnyCable for Rails

Readme

lexxy-realtime

Real-time collaborative editing for Lexxy over Yjs. Drop a <lexxy-collaboration> element inside your <lexxy-editor> and people editing the same document see each other's text, cursors, and selections live.

It works with any Yjs provider. yrby is the recommended one — a reliable, Rails-native provider over Action Cable / AnyCable with ack-tracked delivery (an acknowledged edit isn't silently lost on a flaky connection, and a reconnecting client catches up from the server). But you can just as well point it at a Node y-websocket server, Hocuspocus, y-webrtc, and so on.

Pre-1.0, and tracks pre-1.0 peers (@37signals/lexxy ^0.9, lexical / @lexical/yjs ^0.44). See CHANGELOG.md.

Requirements

  • A Lexxy editor on the page (@37signals/lexxy) — see Lexxy's docs.
  • A Yjs provider and its backend. With yrby that's a Rails channel (see Server); with another provider it's whatever that provider connects to.
  • A JS bundler (jsbundling-rails / esbuild, or any app that bundles its JavaScript). Collaboration relies on one shared copy of lexical and yjs across Lexxy and lexxy-realtime; a bundler dedupes them for you (see a single copy of lexical & yjs).

Install

npm install lexxy-realtime @lexical/yjs yjs y-protocols

You also need a Lexxy editor and lexical (^0.44), which your app already has, plus a provider: for yrby, a cable consumer (@rails/actioncable or @anycable/web); otherwise the provider of your choice (e.g. y-websocket).

Client

lexxy-realtime registers a <lexxy-collaboration> custom element. Create a Yjs doc and a provider, mount the element inside your <lexxy-editor>, and go. The element is the same regardless of provider — only how you build the provider differs.

With yrby (recommended for Rails)

import "@37signals/lexxy";                          // registers <lexxy-editor>
import { YrbyProvider } from "lexxy-realtime";   // registers <lexxy-collaboration>
import * as Y from "yjs";
import { createConsumer } from "@rails/actioncable"; // or "@anycable/web"

const editor = document.querySelector("lexxy-editor");

function startCollaborating() {
  const doc = new Y.Doc();
  const consumer = createConsumer();
  const provider = new YrbyProvider(doc, consumer, "DocumentChannel", { id: documentId });

  const collab = document.createElement("lexxy-collaboration");
  collab.setAttribute("doc-id", documentId);       // Yjs document id (defaults to "main")
  collab.setAttribute("name", currentUserName);    // shown on your cursor to others
  collab.setAttribute("color", "#3b82f6");          // optional cursor color
  collab.doc = doc;
  collab.provider = provider;
  editor.appendChild(collab);

  provider.connect(); // YrbyProvider does not auto-connect
}

// Lexxy sets up its editor asynchronously; start once it's ready.
if (editor.editor) {
  startCollaborating();
} else {
  editor.addEventListener("lexxy:initialize", startCollaborating, { once: true });
}

With any other Yjs provider

Same element — bring your own provider. For example, a Node y-websocket server:

import "@37signals/lexxy";
import "lexxy-realtime"; // registers <lexxy-collaboration>
import * as Y from "yjs";
import { WebsocketProvider } from "y-websocket";

const editor = document.querySelector("lexxy-editor");

function startCollaborating() {
  const doc = new Y.Doc();
  const provider = new WebsocketProvider("wss://your-server", documentId, doc);

  const collab = document.createElement("lexxy-collaboration");
  collab.setAttribute("name", currentUserName);
  collab.doc = doc;
  collab.provider = provider;
  editor.appendChild(collab);
  // y-websocket connects on construction — no connect() call needed.
}

if (editor.editor) startCollaborating();
else editor.addEventListener("lexxy:initialize", startCollaborating, { once: true });

What the element needs from a provider. Any provider with the standard Yjs surface works:

  • provider.awareness — a y-protocols Awareness instance (used for remote cursors/selections).
  • provider.syncedtrue once caught up with the server (used to seed a brand-new, empty document the first time).
  • provider.disconnect() — called when the element is removed.

You start the connection however that provider expects (provider.connect() for YrbyProvider; y-websocket connects on construction). y-websocket, Hocuspocus, and y-webrtc all satisfy this.

Server (yrby)

The recommended path. Collaboration needs a server that records and relays Yjs updates; with yrby that's one Action Cable channel including the yrby-actioncable concern:

# Gemfile: gem "yrby-actioncable"

class DocumentChannel < ApplicationCable::Channel
  include Y::ActionCable::Sync

  # Rebuild a document's state from your store (return nil for a new doc):
  on_load   { |id| Document.find_by(id:)&.yjs_state }
  # Persist each CRDT delta before it's acked/relayed:
  on_change { |id, update| Document.record!(id, update) }

  def subscribed = sync_subscribed(params[:id])
  def receive(data) = sync_receive(data, params[:id])
end

See yrby for durable-store options and the full protocol (reliable delivery, causal-gap handling). Using a different provider instead? Point it at that provider's own backend (e.g. a y-websocket Node server) — nothing on the client above changes except how you build the provider.

Provider API (yrby)

YrbyProvider is a thin alias for yrby-client's ActionCableProvider:

provider.connect();        // open the subscription and start syncing
provider.disconnect();     // pause; queued edits are kept
provider.destroy();        // tear down (also clears presence)

provider.synced;           // caught up with the server?
provider.status;           // "connecting" | "connected" | "synced" | "disconnected"
provider.onStatusChange(({ status }) => render(status)); // returns an unsubscribe fn
provider.awareness;        // the Yjs Awareness instance (presence/cursors)

It owns presence — it creates its own Awareness. Read provider.awareness if you need it (e.g. to show who's here); don't pass one in.

A single copy of lexical & yjs

Lexxy and lexxy-realtime both leave lexical (and lexxy-realtime leaves yjs / @lexical/yjs) as external peers, so your bundler can resolve them to one shared instance. They must be a single copy: Lexical keys node behavior to class identity and Yjs to constructor identity, so two copies break syncing. With matching versions (lexical ^0.44, yjs ^13.6) bundlers dedupe automatically; if yours pulls duplicates, dedupe them (e.g. esbuild --alias:yjs=./node_modules/yjs).

Try it

The yrby Action Cable demo runs a Lexxy editor on lexxy-realtime end to end (there's a one-command Docker setup). Open /docs/demo/lexxy in two windows and type.

Notes

lexxy-realtime applies two small compatibility shims to @lexical/yjs and @37signals/lexxy at runtime, from inside its own bind path — no patch-package, no vendored patches, install the peers and go. They're temporary pending upstream fixes; the details and tracking PRs are in CONTRIBUTING.md.

License

MIT