The Loro extension enables real-time collaborative editing using the Loro CRDT (Conflict-free Replicated Data Type) library. Loro offers excellent performance and a rich set of features for collaborative applications.
Installation
Install Loro peer dependencies:
npm install loro-crdt loro-prosemirror
The Loro extension is included in the prosekit package:
import { defineLoro } from 'prosekit/extensions/loro'
Styling
Import Loro styles to show user selections:
import 'prosekit/extensions/loro/style.css'
Usage
import { createEditor } from 'prosekit/core'
import { defineLoro } from 'prosekit/extensions/loro'
import { Loro } from 'loro-crdt'
import 'prosekit/extensions/loro/style.css'
const loro = new Loro ()
const doc = loro . getMovableList ( 'prosemirror' )
const editor = createEditor ({
extension: defineLoro ({
loro: loro ,
doc: doc
})
})
API reference
defineLoro(options)
Creates a Loro extension for collaborative editing.
The Loro instance to sync with.
The Loro movable list to use for the document. Typically obtained with loro.getMovableList('prosemirror').
options.colors
{ background: string, foreground: string }[]
Color palette for user selections. Defaults to a preset palette.
Complete example
See the Collaboration guide for a complete working example with WebSocket synchronization.
Key concepts
Loro instance
A Loro instance contains shared data structures:
LoroMovableList
The ProseMirror document is stored as a movable list:
const doc = loro . getMovableList ( 'prosemirror' )
Network synchronization
Loro uses binary updates for efficient synchronization:
import { Loro } from 'loro-crdt'
const loro = new Loro ()
// Export updates
const updates = loro . exportFrom ( version )
// Apply updates
loro . import ( updates )
WebSocket sync example
import { Loro } from 'loro-crdt'
const loro = new Loro ()
const ws = new WebSocket ( 'wss://your-server.com/room-id' )
// Send local updates to server
loro . subscribe (( event ) => {
const updates = loro . exportFrom ( event . from )
ws . send ( updates )
})
// Apply remote updates
ws . onmessage = ( event ) => {
const updates = new Uint8Array ( event . data )
loro . import ( updates )
}
User awareness
Track user presence and selections:
// Set local user info
const userId = 'user-123'
loro . setPeerId ( userId )
// Set user metadata
loro . setUserMetadata ({
name: 'Alice' ,
color: '#2563eb' ,
})
Persistence
Save and load document state:
import { Loro } from 'loro-crdt'
// Export snapshot
const snapshot = loro . export ({ mode: 'snapshot' })
localStorage . setItem ( 'document' , JSON . stringify ( Array . from ( snapshot )))
// Load snapshot
const stored = localStorage . getItem ( 'document' )
if ( stored ) {
const snapshot = new Uint8Array ( JSON . parse ( stored ))
loro . import ( snapshot )
}
Time travel
Loro supports history navigation:
// Get current version
const version = loro . version ()
// Checkout previous version
loro . checkout ( version - 1 )
// Return to latest
loro . checkoutToLatest ()
Custom colors
const editor = createEditor ({
extension: defineLoro ({
loro: loro ,
doc: doc ,
colors: [
{ background: '#fbbf2420' , foreground: '#fbbf24' },
{ background: '#3b82f620' , foreground: '#3b82f6' },
{ background: '#ef444420' , foreground: '#ef4444' },
]
})
})
Benefits over Yjs
Better performance - Faster operations on large documents
Time travel - Built-in history navigation
Rich data types - More CRDT types beyond XML
Movable lists - Better support for block reordering
TypeScript - First-class TypeScript support
Use cases
Collaborative documents Multiple users editing the same document
Version history Time travel through document history
Offline-first apps Apps that work without internet
High-performance editing Large documents with many users