Yjs — CRDT Framework for Collaborative Editing

## Overview

25 stars

Best use case

Yjs — CRDT Framework for Collaborative Editing is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

## Overview

Teams using Yjs — CRDT Framework for Collaborative Editing should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/yjs/SKILL.md --create-dirs "https://raw.githubusercontent.com/ComeOnOliver/skillshub/main/skills/TerminalSkills/skills/yjs/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/yjs/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How Yjs — CRDT Framework for Collaborative Editing Compares

Feature / AgentYjs — CRDT Framework for Collaborative EditingStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

## Overview

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Yjs — CRDT Framework for Collaborative Editing


## Overview


Yjs, the high-performance CRDT (Conflict-free Replicated Data Type) framework for building collaborative applications. Helps developers implement real-time document editing, offline-first sync, and peer-to-peer collaboration with automatic conflict resolution.


## Instructions

### Document and Shared Types

Create collaborative data structures that merge automatically:

```typescript
// src/collaboration/document.ts — Set up a collaborative document with shared types
import * as Y from "yjs";

// A Y.Doc is the top-level container for all shared data
// Every connected client gets a copy that stays in sync
const doc = new Y.Doc();

// Y.Text — collaborative rich text (used with editors like Tiptap, ProseMirror)
const yText = doc.getText("document-content");
yText.insert(0, "Hello, ");
yText.insert(7, "world!");
// Result: "Hello, world!" — inserts merge correctly even if concurrent

// Y.Map — collaborative key-value store (like a shared object)
const yMap = doc.getMap("settings");
yMap.set("theme", "dark");
yMap.set("fontSize", 14);
// Two users setting different keys: both applied
// Two users setting the same key: last-write-wins (deterministic)

// Y.Array — collaborative ordered list
const yArray = doc.getArray("tasks");
yArray.push([{ id: "1", title: "Design mockup", done: false }]);
yArray.push([{ id: "2", title: "Implement API", done: false }]);
// Concurrent inserts at different positions: both preserved in correct order

// Y.XmlFragment — collaborative XML tree (for rich text editors)
const yXml = doc.getXmlFragment("rich-content");
// Used internally by editor bindings (Tiptap, ProseMirror, Slate)

// Nested structures — Y types can be nested arbitrarily
const yNestedMap = new Y.Map();
yNestedMap.set("status", "active");
yMap.set("project", yNestedMap);  // Map inside a map
```

### WebSocket Provider

Connect clients through a WebSocket server for real-time sync:

```typescript
// src/collaboration/provider.ts — WebSocket-based real-time sync
import * as Y from "yjs";
import { WebsocketProvider } from "y-websocket";

const doc = new Y.Doc();

// Connect to a y-websocket server
// All clients in the same room sync automatically
const provider = new WebsocketProvider(
  "wss://your-yjs-server.example.com",  // WebSocket server URL
  "document-room-123",                   // Room name — clients in same room sync
  doc,
  {
    connect: true,                        // Auto-connect on creation
    params: { token: "auth-token-here" }, // Auth params sent on connect
  }
);

// Awareness — lightweight presence data (cursors, selections, user info)
// Unlike document state, awareness is ephemeral (not persisted)
const awareness = provider.awareness;

awareness.setLocalStateField("user", {
  name: "Alice",
  color: "#ff5733",
  cursor: null,
});

// Listen to other users' awareness changes
awareness.on("change", () => {
  const states = awareness.getStates();  // Map<clientId, state>
  states.forEach((state, clientId) => {
    if (clientId !== doc.clientID) {
      console.log(`User ${state.user?.name} is connected`);
    }
  });
});

// Connection status
provider.on("status", ({ status }: { status: string }) => {
  console.log(`Connection: ${status}`);  // "connecting" | "connected" | "disconnected"
});

// Sync status — fires when initial sync with server is complete
provider.on("sync", (isSynced: boolean) => {
  if (isSynced) console.log("Document fully synced with server");
});
```

### Server-Side Setup

Run a y-websocket server for document persistence:

```typescript
// server/yjs-server.ts — WebSocket server with persistence
import { WebSocketServer } from "ws";
import { setupWSConnection, setPersistence } from "y-websocket/bin/utils";
import * as Y from "yjs";
import { MongodbPersistence } from "y-mongodb-provider";

const wss = new WebSocketServer({ port: 1234 });

// Persist documents to MongoDB (survives server restarts)
const mdb = new MongodbPersistence(process.env.MONGODB_URL!, {
  collectionName: "yjs-documents",
  flushSize: 100,          // Batch 100 updates before flushing to DB
  multipleCollections: true, // Separate collection per document for performance
});

setPersistence({
  bindState: async (docName: string, ydoc: Y.Doc) => {
    // Load existing document state from MongoDB
    const persistedDoc = await mdb.getYDoc(docName);
    const persistedState = Y.encodeStateAsUpdate(persistedDoc);
    Y.applyUpdate(ydoc, persistedState);

    // Save updates as they happen
    ydoc.on("update", (update: Uint8Array) => {
      mdb.storeUpdate(docName, update);
    });
  },
  writeState: async (docName: string, ydoc: Y.Doc) => {
    // Called when all clients disconnect — final persistence
    await mdb.flushDocument(docName);
  },
});

wss.on("connection", (ws, req) => {
  // Authenticate the connection
  const token = new URL(req.url!, "http://localhost").searchParams.get("token");
  if (!verifyToken(token)) {
    ws.close(4001, "Unauthorized");
    return;
  }

  setupWSConnection(ws, req);
});

console.log("Yjs WebSocket server running on port 1234");
```

### Editor Integration (Tiptap)

Add collaborative editing to a Tiptap rich text editor:

```tsx
// src/components/CollaborativeEditor.tsx — Tiptap with Yjs collaboration
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Collaboration from "@tiptap/extension-collaboration";
import CollaborationCursor from "@tiptap/extension-collaboration-cursor";
import * as Y from "yjs";
import { WebsocketProvider } from "y-websocket";

interface Props {
  documentId: string;
  userName: string;
  userColor: string;
}

export function CollaborativeEditor({ documentId, userName, userColor }: Props) {
  const doc = useMemo(() => new Y.Doc(), []);
  const provider = useMemo(
    () => new WebsocketProvider("wss://yjs.example.com", documentId, doc),
    [doc, documentId]
  );

  const editor = useEditor({
    extensions: [
      StarterKit.configure({
        history: false,    // Disable default history — Yjs handles undo/redo
      }),
      Collaboration.configure({
        document: doc,     // Bind editor content to Yjs document
      }),
      CollaborationCursor.configure({
        provider,          // Share cursor positions via awareness
        user: { name: userName, color: userColor },
      }),
    ],
  });

  // Clean up on unmount
  useEffect(() => {
    return () => {
      provider.destroy();
      doc.destroy();
    };
  }, [doc, provider]);

  return (
    <div className="editor-container">
      <EditorContent editor={editor} />
      <ConnectionStatus provider={provider} />
    </div>
  );
}

function ConnectionStatus({ provider }: { provider: WebsocketProvider }) {
  const [status, setStatus] = useState("connecting");

  useEffect(() => {
    const handler = ({ status }: { status: string }) => setStatus(status);
    provider.on("status", handler);
    return () => provider.off("status", handler);
  }, [provider]);

  return (
    <div className={`status-badge ${status}`}>
      {status === "connected" ? "🟢 Connected" : "🔴 Reconnecting..."}
    </div>
  );
}
```

### Offline Support and Sync

Handle offline editing with automatic merge on reconnect:

```typescript
// src/collaboration/offline.ts — IndexedDB persistence for offline support
import * as Y from "yjs";
import { IndexeddbPersistence } from "y-indexeddb";
import { WebsocketProvider } from "y-websocket";

const doc = new Y.Doc();

// IndexedDB provider — saves document locally in the browser
// Changes made offline are preserved and synced when reconnected
const indexedDb = new IndexeddbPersistence("my-app-docs", doc);

indexedDb.on("synced", () => {
  console.log("Local data loaded from IndexedDB");
});

// WebSocket provider — syncs with other clients when online
const wsProvider = new WebsocketProvider("wss://yjs.example.com", "doc-123", doc);

// The two providers work together:
// 1. Online: changes sync via WebSocket AND save to IndexedDB
// 2. Offline: changes save to IndexedDB only
// 3. Reconnect: IndexedDB state syncs with server, merging all changes

// Observe document changes (from any source: local, remote, or loaded from DB)
doc.on("update", (update: Uint8Array, origin: any) => {
  if (origin === "local") {
    console.log("Local change");
  } else {
    console.log("Remote change received");
  }
});
```

## Installation

```bash
# Core library
npm install yjs

# Providers (pick what you need)
npm install y-websocket          # WebSocket sync
npm install y-indexeddb           # Browser offline persistence
npm install y-webrtc              # Peer-to-peer sync (no server)

# Editor bindings
npm install @tiptap/extension-collaboration @tiptap/extension-collaboration-cursor

# Server persistence
npm install y-mongodb-provider    # MongoDB
npm install y-leveldb             # LevelDB (lightweight)
```


## Examples


### Example 1: Setting up Yjs with a custom configuration

**User request:**

```
I just installed Yjs. Help me configure it for my TypeScript + React workflow with my preferred keybindings.
```

The agent creates the configuration file with TypeScript-aware settings, configures relevant plugins/extensions for React development, sets up keyboard shortcuts matching the user's preferences, and verifies the setup works correctly.

### Example 2: Extending Yjs with custom functionality

**User request:**

```
I want to add a custom websocket provider to Yjs. How do I build one?
```

The agent scaffolds the extension/plugin project, implements the core functionality following Yjs's API patterns, adds configuration options, and provides testing instructions to verify it works end-to-end.


## Guidelines

1. **Choose the right shared type** — Y.Text for documents, Y.Map for settings/state, Y.Array for lists; don't force everything into one type
2. **Keep documents small** — Large Y.Docs (>10MB) impact performance; split content into multiple documents
3. **Use awareness for ephemeral data** — Cursors, selections, and typing indicators belong in awareness, not document state
4. **Always add offline persistence** — y-indexeddb prevents data loss on disconnect; it's one line of code
5. **Authenticate at the provider level** — Validate tokens in the WebSocket server before allowing sync
6. **Batch observations** — Use `doc.transact()` to group multiple changes into one update event
7. **Garbage collect** — Call `doc.gc = true` to enable garbage collection of deleted content
8. **Test concurrent edits** — Open multiple browser tabs and edit simultaneously; verify merge behavior

Related Skills

defold-shaders-editing

25
from ComeOnOliver/skillshub

Creates and edits Defold shader files (.vp, .fp, .glsl). Use when asked to create, modify, or configure any Defold vertex shader, fragment shader, or GLSL include file.

defold-scripts-editing

25
from ComeOnOliver/skillshub

Creates and edits Defold Lua script files (.script, .gui_script, .render_script, .editor_script) and plain Lua modules (.lua). Use when asked to create, modify, or configure any Defold script or Lua module.

defold-proto-file-editing

25
from ComeOnOliver/skillshub

Creates and edits Defold resource and component files that use Protobuf Text Format (.collection, .go, .atlas, .sprite, .gui, .collisionobject, .convexshape, .label, .font, .material, .model, .mesh, .particlefx, .sound, .camera, .factory, .collectionfactory, .collectionproxy, .tilemap, .tilesource, .objectinterpolation). Use when asked to create, modify, or configure any Defold proto text format file.

defold-native-extension-editing

25
from ComeOnOliver/skillshub

Defold native extension development. Use when creating or editing C/C++ (.c, .cpp, .h, .hpp), JavaScript (.js), or manifest files in native extension directories (src/, include/, lib/, api/).

microsoft-agent-framework

25
from ComeOnOliver/skillshub

Create, update, refactor, explain, or review Microsoft Agent Framework solutions using shared guidance plus language-specific references for .NET and Python.

containerize-aspnet-framework

25
from ComeOnOliver/skillshub

Containerize an ASP.NET .NET Framework project by creating Dockerfile and .dockerfile files customized for the project.

startup-metrics-framework

25
from ComeOnOliver/skillshub

This skill should be used when the user asks about "key startup metrics", "SaaS metrics", "CAC and LTV", "unit economics", "burn multiple", "rule of 40", "marketplace metrics", or requests guidance on tracking and optimizing business performance metrics.

framework-migration-legacy-modernize

25
from ComeOnOliver/skillshub

Orchestrate a comprehensive legacy system modernization using the strangler fig pattern, enabling gradual replacement of outdated components while maintaining continuous business operations through ex

framework-migration-deps-upgrade

25
from ComeOnOliver/skillshub

You are a dependency management expert specializing in safe, incremental upgrades of project dependencies. Plan and execute dependency updates with minimal risk, proper testing, and clear migration pa

framework-migration-code-migrate

25
from ComeOnOliver/skillshub

You are a code migration expert specializing in transitioning codebases between frameworks, languages, versions, and platforms. Generate comprehensive migration plans, automated migration scripts, and

data-quality-frameworks

25
from ComeOnOliver/skillshub

Implement data quality validation with Great Expectations, dbt tests, and data contracts. Use when building data quality pipelines, implementing validation rules, or establishing data contracts.

backtesting-frameworks

25
from ComeOnOliver/skillshub

Build robust backtesting systems for trading strategies with proper handling of look-ahead bias, survivorship bias, and transaction costs. Use when developing trading algorithms, validating strategies, or building backtesting infrastructure.