A collaborative editing plugin for Lexical Rich Editor built with Loro CRDT, providing real-time collaborative editing capabilities with conflict-free synchronization.
This package provides three main components for building collaborative text editors:
LoroCollaborativePlugin.tsx
- A Lexical plugin that integrates Loro CRDT for real-time collaborative editingLexicalModel
Python Library - A standalone document model for Lexical content with CRDT capabilitieslexical-loro
WebSocket Server - A Python server using loro-py for real-time collaboration
import { LoroCollaborativePlugin } from './src/LoroCollaborativePlugin';
function MyEditor() {
return (
<LexicalComposer initialConfig={editorConfig}>
<RichTextPlugin />
<LoroCollaborativePlugin
websocketUrl="ws://localhost:8081"
docId="my-document"
username="user1"
/>
</LexicalComposer>
);
}
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")
# Install the Python package
pip install -e .
# Start the server
lexical-loro-server --port 8081
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.
- π 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
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
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
Install the Python WebSocket server:
# Install from this repository
pip install -e .
# Or install specific dependencies
pip install websockets click loro
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>
);
}
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 manipulationexamples/file_sync_example.py
- File persistence and batch operationsexamples/collaboration_example.py
- Simulating collaborative editingdocs/LEXICAL_MODEL_GUIDE.md
- Comprehensive documentation
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
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())
For detailed API documentation, see docs/API.md
.
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)
}
See docs/INITIALIZATION_GUIDE.md
for comprehensive guidance on:
- Proper plugin ordering
- Initialization callbacks
- Error handling
- Common anti-patterns to avoid
For complete working examples and demonstrations, see the src/examples/
directory:
# Run the example application
npm install
npm run example
# This starts both Node.js and Python servers plus a React demo app
# Open http://localhost:5173 to see dual editor interface
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.
src/
βββ LoroCollaborativePlugin.tsx # Main Lexical plugin for collaboration
βββ vite-env.d.ts # TypeScript definitions
lexical_loro/ # Python WebSocket server package
βββ __init__.py # Package exports
βββ server.py # WebSocket server implementation
βββ cli.py # Command line interface
βββ model/
β βββ lexical_model.py # Standalone LexicalModel library
βββ tests/ # Python test suite
docs/
βββ LEXICAL_MODEL_GUIDE.md # Comprehensive library documentation
examples/
βββ memory_only_example.py # Basic LexicalModel usage
βββ file_sync_example.py # File persistence example
βββ collaboration_example.py # Collaborative editing simulation
βββ README.md # Examples documentation
pyproject.toml # Python package configuration
src/examples/ # Complete demo application
βββ App.tsx # Demo app with dual editors
βββ LexicalCollaborativeEditor.tsx # Rich text editor example
βββ TextAreaCollaborativeEditor.tsx # Simple text editor example
βββ ServerSelector.tsx # Server selection UI
βββ LexicalToolbar.tsx # Rich text toolbar
βββ main.tsx # Demo app entry point
βββ *.css # Styling for examples
servers/
βββ server.ts # Node.js server (for comparison)
src/archive/ # Historical plugin implementations
βββ LoroCollaborativePlugin0.tsx # Previous versions for reference
βββ LoroCollaborativePlugin1.tsx
βββ LoroCollaborativePlugin2.tsx
βββ LoroCollaborativePlugin3.tsx
βββ LoroCollaborativePlugin4.tsx
βββ LoroCollaborativePlugin5.tsx
For detailed architecture documentation, see docs/ARCHITECTURE.md
.
The collaboration system consists of three main components:
- LoroCollaborativePlugin (Client-side) - Lexical integration
- LoroWebSocketServer (Server-side) - Real-time synchronization
- LexicalModel (Standalone Library) - Independent document model
User Types β Lexical Editor β Plugin β Loro CRDT β WebSocket
β
WebSocket β Loro CRDT β Plugin β Lexical Editor β Other Users
For detailed configuration options, see docs/API.md
.
// Plugin configuration
<LoroCollaborativePlugin
websocketUrl="ws://localhost:8081"
docId="my-document"
username="user123"
debug={true}
/>
# Server configuration
lexical-loro-server --port 8081 --log-level DEBUG
For comprehensive development guidelines, see docs/DEVELOPMENT.md
.
# Install dependencies
npm install
pip install -e ".[dev]"
# Run tests
npm test
npm run test:py
# Start development server
lexical-loro-server --log-level DEBUG
We welcome contributions! Please see docs/DEVELOPMENT.md
for detailed guidelines.
- Fork the repository
- Create a feature branch
- Focus changes on core components
- Add tests for new functionality
- Update documentation as needed
- Submit a pull request
- API Documentation - Complete API reference
- Initialization Guide - Best practices for setup
- Architecture - System design and data flow
- Development Guide - Contributing and development setup
- LexicalModel Guide - Standalone library documentation
This project is open source and available under the MIT License.