Skip to main content

Datalayer

Become a Sponsor

✍️ 🦜 Collaborative Plugin for Lexical with Loro CRDT

A collaborative editing plugin for Lexical Rich Editor built with Loro CRDT, providing real-time collaborative editing capabilities with conflict-free synchronization.

Core Components

This package provides three main components for building collaborative text editors:

  1. LoroCollaborativePlugin.tsx - A Lexical plugin that integrates Loro CRDT for real-time collaborative editing
  2. LexicalModel Python Library - A standalone document model for Lexical content with CRDT capabilities
  3. lexical-loro WebSocket Server - A Python server using loro-py for real-time collaboration

Quick Start

Using the Lexical Plugin

import { LoroCollaborativePlugin } from './src/LoroCollaborativePlugin';

function MyEditor() {
  return (
    <LexicalComposer initialConfig={editorConfig}>
      <RichTextPlugin />
      <LoroCollaborativePlugin 
        websocketUrl="ws://localhost:8081"
        docId="my-document"
        username="user1"
      />
    </LexicalComposer>
  );
}

Using the LexicalModel Library

from lexical_loro import LexicalModel

# Create a new document
model = LexicalModel.create_document("my-document")

# Add content
model.add_block({
    "text": "My Document",
    "format": 0,
    "style": ""
}, "heading1")

model.add_block({
    "text": "This is a paragraph.",
    "format": 0,
    "style": ""
}, "paragraph")

# Save to file
model.save_to_file("document.json")

# Load from file
loaded_model = LexicalModel.load_from_file("document.json")

Using the Python Server

# Install the Python package
pip install -e .

# Start the server
lexical-loro-server --port 8081

Examples

For complete working examples, see the src/examples/ directory which contains:

  • Full React application with dual editor support
  • Server selection interface
  • Connection status indicators
  • Rich text formatting examples

DISCLAIMER Collaborative Cursors still need fixes, see this issue.

Core Features

  • 🔄 Real-time Collaboration: Multiple users can edit the same document simultaneously
  • 🚀 Conflict-free: Uses Loro CRDT to automatically resolve conflicts
  • 📝 Lexical Integration: Seamless integration with Lexical rich text editor
  • 📚 Standalone Library: Use LexicalModel independently for document management
  • 🌐 WebSocket Server: Python server for maintaining document state
  • 📡 Connection Management: Robust WebSocket connection handling
  • ✨ Rich Text Support: Preserves formatting during collaborative editing
  • 💾 Serialization: JSON export/import and file persistence
  • 🔧 Extensible: Plugin-based architecture for easy customization

Technology Stack

Core Dependencies:

  • Lexical: v0.33.1 (Facebook's extensible text editor framework)
  • Loro CRDT: v1.5.10 (Conflict-free replicated data types)
  • React: 18/19 (for plugin hooks and components)
  • Python: 3.8+ with loro-py and websockets

Development Dependencies:

  • TypeScript: For type safety
  • Vite: For building and development (examples only)
  • pytest: Python testing
  • ESLint: Code linting

Installation

Core Plugin

The Lexical plugin is a single TypeScript/React component that you can copy into your project:

# Copy the plugin file
cp src/LoroCollaborativePlugin.tsx your-project/src/

Dependencies required:

npm install lexical @lexical/react @lexical/selection loro-crdt react react-dom

Python Server

Install the Python WebSocket server:

# Install from this repository
pip install -e .

# Or install specific dependencies
pip install websockets click loro

Usage

1. Lexical Plugin Integration

Add the plugin to your Lexical editor:

import { LexicalComposer } from '@lexical/react/LexicalComposer';
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import { LoroCollaborativePlugin } from './LoroCollaborativePlugin';

const editorConfig = {
  namespace: 'MyEditor',
  theme: {},
  onError: console.error,
};

function CollaborativeEditor() {
  return (
    <LexicalComposer initialConfig={editorConfig}>
      <div className="editor-container">
        <RichTextPlugin
          contentEditable={<ContentEditable className="editor-input" />}
          placeholder={<div className="editor-placeholder">Start typing...</div>}
          ErrorBoundary={() => <div>Error occurred</div>}
        />
        <LoroCollaborativePlugin 
          websocketUrl="ws://localhost:8081"
          docId="shared-document"
          username="user123"
        />
      </div>
    </LexicalComposer>
  );
}

2. Standalone LexicalModel Library

Use the LexicalModel library independently for document management:

from lexical_loro import LexicalModel

# Create a new document
model = LexicalModel.create_document("my-document")

# Add different types of content
model.add_block({
    "text": "My Document",
    "format": 0,
    "style": ""
}, "heading1")

model.add_block({
    "text": "This is a paragraph with **bold** text.",
    "format": 0,
    "style": ""
}, "paragraph")

model.add_block({
    "text": "",
    "format": 0,
    "style": ""
}, "list")

# Serialize to JSON
json_data = model.to_json()

# Save to file
model.save_to_file("document.json")

# Load from file
loaded_model = LexicalModel.load_from_file("document.json")

# Access blocks
for block in loaded_model.get_blocks():
    print(f"{block['type']}: {block.get('text', '')}")

For more examples, see:

  • examples/memory_only_example.py - Basic document creation and manipulation
  • examples/file_sync_example.py - File persistence and batch operations
  • examples/collaboration_example.py - Simulating collaborative editing
  • docs/LEXICAL_MODEL_GUIDE.md - Comprehensive documentation

3. Python Server Setup

Start the WebSocket server:

# Default port (8081)
lexical-loro-server

# Custom port
lexical-loro-server --port 8082

# With debug logging
lexical-loro-server --port 8081 --log-level DEBUG

4. Programmatic Server Usage

import asyncio
from lexical_loro import LoroWebSocketServer

async def main():
    server = LoroWebSocketServer(port=8081)
    await server.start()
    print("Server running on ws://localhost:8081")

if __name__ == "__main__":
    asyncio.run(main())

Plugin API

For detailed API documentation, see docs/API.md.

Quick Reference

interface LoroCollaborativePluginProps {
  websocketUrl: string;          // WebSocket server URL
  docId: string;                 // Unique document identifier
  username: string;              // User identifier
  userColor?: string;            // User cursor color (optional)
  debug?: boolean;               // Enable debug logging (optional)
}

Initialization Best Practices

⚠️ Important: Always wait for collaboration initialization before enabling other plugins.

See docs/INITIALIZATION_GUIDE.md for comprehensive guidance on:

  • Proper plugin ordering
  • Initialization callbacks
  • Error handling
  • Common anti-patterns to avoid

Examples

For complete working examples and demonstrations, see the src/examples/ directory:

# Run the example application
npm install
make example

# This starts both Node.js and Python servers plus a React demo app
# Open http://localhost:3000/split/index.html to see the 2 editors view

The examples include:

  • Complete React App: Full collaborative editor with UI
  • Server Selection: Switch between Node.js and Python backends
  • Dual Editors: Simple text area and rich Lexical editor
  • Real-time Demo: Multi-user collaboration testing

See src/examples/README.md for detailed example documentation.

Architecture

EDITOR 1                                           EDITOR 2

loro                                               loro
- node(data: root(1))                              - node(data: root(12))
  - node(data: element(2))                           - node(data: element(22))
    - node(data: text(3))                              - node(data: text(13))
  - node(data: counter(4))                           - node(data: counter(4))

                <---- loro updates via websocket ------>
                <---- loro node ids are the same ------>
             <---- lexical node keys are different ------>

lexical                                            lexical
- root(1)                                          - root(12)
  - element(2)                                       - element(22)
    - text(3)                                          - text(13)
  - counter(4)                                       - counter(49)

Examples

Loro examples

  • http://localhost:3000/?isCollab=true

  • http://localhost:3000/split/?isCollab=true

Y.js examples (for reference)

  • http://localhost:3000/?isCollab=true&useYjs=true

  • http://localhost:3000/split/?isCollab=true&useYjs=true

Metadata

Release files for lexical-loro 1.0.8

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lexical-loro 1.0.8
File Size Uploaded Provenance Actions
lexical_loro-1.0.8.tar.gz 56.0 kB With provenance Details for lexical_loro-1.0.8.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for lexical-loro 1.0.8
File Interpreter ABI Platform Actions
lexical_loro-1.0.8-py3-none-any.whl Python 3 none any Details for lexical_loro-1.0.8-py3-none-any.whl

Total release size: 116.4 kB

Release files / lexical_loro-1.0.8.tar.gz

Download URL lexical_loro-1.0.8.tar.gz
Size 56.0 kB
Tags Source
SHA-256 checksum
How to use checksums
5fe165d04f81d27a1eb4d5fd46042a4e97a50618ef3182840db94f53e604fb43
BLAKE2b-256 checksum
How to use checksums
f53af32fc3b55b552fdecf5457fe4feb5aca2235382166471d79c9cb5079ff7d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Release files / lexical_loro-1.0.8-py3-none-any.whl

Download URL lexical_loro-1.0.8-py3-none-any.whl
Size 60.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a9d270fcd396385e3e11db1a28b663b1c689071ad2184adbaaed19745dfb5fca
BLAKE2b-256 checksum
How to use checksums
e4b0504a554012cfd8a9bd85e53649474c68a52413a5e1bad9991d608dc25a4f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Security for lexical-loro 1.0.8

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for lexical_loro-1.0.8.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page