CRDT · RGA/YATA

Collaborative Editing

Syncline Editor ships a full CRDT engine based on the RGA/YATA algorithm. Add a collab config block and remote cursors, selections, and peer presence are rendered automatically — no extra dependencies required.

Quick start

The fastest way to try collaboration is with LocalTransport, which syncs two editor instances on the same page without any server.

import { createEditor, LocalTransport } from '@synclineapi/editor';

// Both editors must share the same room string.
const ROOM = 'my-document';

const editorA = createEditor(containerA, {
  value: '// Alice is here',
  language: 'typescript',
  collab: {
    transport: new LocalTransport(ROOM),
    name: 'Alice',
    onPeersChange(peers) { console.log([...peers.values()]); },
  },
});

const editorB = createEditor(containerB, {
  value: '// Bob joins',
  language: 'typescript',
  collab: {
    transport: new LocalTransport(ROOM),
    name: 'Bob',
  },
});

Transports

All three transports implement the same CRDTTransport interface, so you can swap them without changing any other code.

TransportScopeServer needed?Typical use
LocalTransportSame pageNoSplit-view demos, in-page diffing, tests
BroadcastChannelTransportSame origin, cross-tabNoCross-tab sync, offline-first apps
WebSocketTransportAny device / networkYes (any WS relay)Production multi-user editing

LocalTransport — same page

Fastest option. Both editor instances share a simple in-memory broadcast bus keyed by the room string. Ideal for split-view demos and automated tests.

import { createEditor, LocalTransport } from '@synclineapi/editor';

// Both editors must share the same room string.
const ROOM = 'my-document';

const editorA = createEditor(containerA, {
  value: '// Alice is here',
  language: 'typescript',
  collab: {
    transport: new LocalTransport(ROOM),
    name: 'Alice',
    onPeersChange(peers) { console.log([...peers.values()]); },
  },
});

const editorB = createEditor(containerB, {
  value: '// Bob joins',
  language: 'typescript',
  collab: {
    transport: new LocalTransport(ROOM),
    name: 'Bob',
  },
});

BroadcastChannelTransport — cross-tab

Uses the browser BroadcastChannel API (same origin) to sync edits across multiple tabs and windows — no server required. Works in all modern browsers.

import { createEditor, BroadcastChannelTransport } from '@synclineapi/editor';

// Works across browser tabs on the same origin — no server required.
const editor = createEditor(container, {
  value: '',
  language: 'typescript',
  collab: {
    transport: new BroadcastChannelTransport('my-channel'),
    name: 'Alice',
    onStatus(status) {
      console.log('collab:', status); // 'connecting' | 'connected' | …
    },
  },
});

WebSocketTransport — cross-device

Connect to any WebSocket relay (your own server or a hosted one). The transport handles reconnections automatically: the CRDT state vector ensures no operations are lost after a reconnect.

import { createEditor, WebSocketTransport } from '@synclineapi/editor';

// Point at any ws/wss relay server.
const editor = createEditor(container, {
  value: '',
  language: 'typescript',
  collab: {
    transport: new WebSocketTransport('wss://relay.example.com', 'my-doc'),
    name: 'Alice',
    onPeersChange(peers) {
      // peers: Map<siteId, AwarenessState>
      for (const [id, state] of peers) {
        console.log(id, state.name, state.color, state.cursor);
      }
    },
    onStatus(status) {
      if (status === 'error') reconnect();
    },
  },
});

Dynamic connect / disconnect

You can enable, swap, or disable collaboration at any time on an existing editor instance using editor.updateConfig().

// Connect or reconnect at any time after creation:
editor.updateConfig({
  collab: {
    transport: new BroadcastChannelTransport('new-room'),
    name: 'Carol',
  },
});

// Disconnect and clean up:
editor.updateConfig({ collab: undefined });

CollabConfig reference

import type { CollabConfig } from '@synclineapi/editor';

const collabConfig: CollabConfig = {
  /** A CRDTTransport instance */
  transport: new BroadcastChannelTransport('room'),

  /** Display name shown to other peers. Defaults to a random ID. */
  name: 'Alice',

  /**
   * Called whenever the peer roster changes (someone joins or leaves,
   * or any awareness state is updated).
   */
  onPeersChange(peers: Map<string, AwarenessState>): void { /* … */ },

  /**
   * Connection lifecycle callback.
   * Possible values: 'connecting' | 'connected' | 'disconnected' | 'error'
   */
  onStatus(status: string): void { /* … */ },
};

AwarenessState shape

The map passed to onPeersChange contains one entry per connected peer (excluding the local user). Each value is an AwarenessState:

// The onPeersChange callback receives a Map<siteId, AwarenessState>
// where AwarenessState looks like:
interface AwarenessState {
  /** Unique site identifier (UUID) */
  siteId: string;
  /** Display name set in CollabConfig.name */
  name: string;
  /** Hex colour auto-assigned to this peer */
  color: string;
  /** Current cursor position (line/col) or null when unfocused */
  cursor: { line: number; col: number } | null;
  /** Current selection range or null */
  selection: { anchor: { line: number; col: number }; head: { line: number; col: number } } | null;
  /** ISO timestamp of last update */
  updatedAt: string;
}

The color field is deterministic — it is derived from the peer's site ID so it stays stable across reconnects and page reloads.

How it works

Algorithm: Each character is represented as a CRDT node with a globally unique ID (site + clock). Insertions use YATA tie-breaking, and deletions are tombstoned — guaranteeing convergence with no central authority.

State vectors: Each peer tracks a state vector (logical clock per site). On reconnect, only the missing operations are exchanged, keeping reconnection bandwidth minimal.

Awareness: Cursor and selection positions are broadcast as ephemeral awareness messages — they are not part of the persistent CRDT document and do not increase document size over time.

Syncline Editor

© 2026 Syncline Editor. All rights reserved.