What Is YJS and What Role Does It Play in AFFiNE?

YJS is an open-source CRDT (Conflict-free Replicated Data Type) library that serves as the real-time collaboration engine in the AFFiNE knowledge base, enabling automatic conflict resolution, offline-first persistence, and synchronous multi-user editing across web and desktop platforms.

AFFiNE is an open-source knowledge management platform that unifies documents, whiteboards, and databases in a collaborative workspace. At the heart of its architecture lies YJS, a JavaScript CRDT implementation that powers every collaborative feature without requiring complex server-side Operational Transformation logic. Understanding how YJS integrates into AFFiNE reveals the technical foundation behind its seamless real-time synchronization and offline editing capabilities.

Understanding YJS as a CRDT Foundation

YJS implements Conflict-free Replicated Data Types, mathematical structures that guarantee all replicas of a document will converge to the same state regardless of the order in which updates are applied. Unlike traditional locking mechanisms or Operational Transformation (OT), CRDTs allow users to edit documents offline and merge changes deterministically when reconnecting, making YJS ideal for AFFiNE's offline-first architecture.

Four Critical Roles of YJS in AFFiNE

The AFFiNE codebase leverages YJS across four primary domains:

  • Live, synchronous editing – Every keystroke and block modification in AFFiNE is applied to a Y.Doc instance, encoded as binary updates, and propagated to peers via WebRTC or WebSocket connections.
  • Automatic conflict resolution – Through native YJS types like YMap, YArray, and YText, concurrent edits from multiple users are merged mathematically without manual intervention or error-prone reconciliation logic.
  • Persistence and offline-first – Binary YJS updates are stored locally in IndexedDB through AFFiNE's YjsDBAdapter, allowing users to work offline and sync seamlessly when reconnecting.
  • Cross-platform consistency – The same YJS document format runs identically on web clients, Electron desktop applications, and mobile platforms, ensuring collaborative state remains synchronized across environments.

Where YJS Lives in the AFFiNE Codebase

AFFiNE wraps YJS in multiple abstraction layers to bridge the low-level CRDT operations with high-level UI components.

Workspace Implementation

The core workspace logic resides in packages/frontend/core/src/modules/workspace/impls/workspace.ts, where WorkspaceImpl instantiates a Y.Doc for each workspace and connects it to the UI layer.

Document Layer

In packages/frontend/core/src/modules/workspace/impls/doc.ts, AFFiNE maintains the root YJS map that stores all blocks, page metadata, and collaborative state, providing convenient wrappers around native YJS types.

ORM Adapters

The YjsDBAdapter class in packages/common/infra/src/orm/core/adapters/yjs/db.ts bridges YJS documents to AFFiNE's ORM-like database abstraction, exposing table-style CRUD operations that internally read and write YJS updates.

Table Adapter

For structured data access, packages/common/infra/src/orm/core/adapters/yjs/table.ts provides type-safe interfaces to YJS-backed tables, handling workspace properties and collection data with validation logic.

Sync Engine

The Blocksuite framework's reactive layer at blocksuite/framework/store/src/reactive/flat-native-y/* wraps YJS maps and arrays in reactive proxies, enabling Vue and React components to re-render instantly when collaborative changes arrive.

Snapshot Tooling

For debugging and migrations, packages/tools/doc-diff/src/index.ts implements utilities like readYjsDocFromFile that read binary YJS snapshots, extract tables, and produce diffs between document versions.

How AFFiNE Processes YJS Updates

The internal data flow follows a predictable pipeline from user action to persistence:

  1. Document creation – AFFiNE instantiates new Y.Doc() when creating a workspace in WorkspaceImpl.
  2. Block attachment – Each block is stored as a YMap or YArray inside the root YMap managed in the document layer.
  3. Update listening – The system registers doc.on('update', handler) callbacks that encode changes using encodeStateAsUpdate and write binary data to IndexedDB via YjsDBAdapter.
  4. Peer broadcasting – The same binary update is transmitted over the network to other clients, which apply it locally using applyUpdate.
  5. UI reactivity – Blocksuite's reactive layer converts YJS events into framework-specific state updates, causing immediate re-renders without manual DOM manipulation.

Working with YJS in AFFiNE: Code Example

The following TypeScript snippet demonstrates the core patterns used throughout AFFiNE's workspace implementation:

import * as Y from 'yjs';
import { applyUpdate, encodeStateAsUpdate } from 'yjs';
import { YjsDBAdapter } from '@toeverything/infra';

// 1️⃣ Create a new Yjs document (workspace root)
const ydoc = new Y.Doc();

// 2️⃣ Add a shared map that will hold page blocks
const pages = ydoc.getMap('pages');

// 3️⃣ Insert a block (a simple text block)
const block = new Y.Map();
block.set('type', 'paragraph');
block.set('content', new Y.Text('Hello, AFFiNE!'));
pages.set('page-1', block);

// 4️⃣ Persist the document (AFFiNE uses YjsDBAdapter under the hood)
const dbAdapter = new YjsDBAdapter(/* schema, provider */);
await dbAdapter.save('workspace-123', ydoc); // internally calls encodeStateAsUpdate

// 5️⃣ Later, on another client, load & apply the update
const binary = await dbAdapter.load('workspace-123');
const otherDoc = new Y.Doc();
applyUpdate(otherDoc, binary);

// 6️⃣ Listen for remote updates
otherDoc.on('update', update => {
  // broadcast to other peers

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →