Loro
Loro is a high‑performance CRDT framework for local‑first apps that keeps state consistent across devices and users, works offline and in real time, automatically merges conflicts, and enables undo/redo and time travel.
Loro is a high-performance CRDTs library offering Rust, JavaScript and Swift APIs.
Common Tasks & Examples
Getting Started
- Create a document:
LoroDoc::new()- Initialize a new collaborative document - Add containers:
get_text,get_list,get_map,get_tree,get_movable_list,get_counter(feature "counter") - Listen to changes:
subscribeorsubscribe_root- React to document/container modifications - Export/Import state:
exportandimport- Save and load documents
Real-time Collaboration
- Sync between peers:
export(ExportMode::updates(&vv))+import- Exchange incremental updates (seeExportMode::updates) - Stream updates:
subscribe_local_update- Send changes over WebSocket/WebRTC - Set unique peer ID:
set_peer_id- Ensure each client has a unique identifier - Handle conflicts: Automatic - All Loro data types are CRDTs that merge concurrent edits
Rich Text Editing
- Create rich text:
get_text- Initialize a collaborative text container - Edit text:
LoroText::insert,LoroText::delete,LoroText::apply_delta - Apply formatting:
LoroText::mark- Add bold, italic, links, custom styles - Track cursor positions:
LoroText::get_cursor+LoroDoc::get_cursor_pos- Stable positions across edits - Configure styles:
config_text_style/config_default_text_style- Define expand behavior for marks
Data Structures
- Ordered lists:
get_list- Arrays withpush,insert,delete - Key-value maps:
get_map- Objects withinsert,get,delete - Hierarchical trees:
get_tree- Trees withcreate,mov,mov_to - Reorderable lists:
get_movable_list- Drag-and-drop withmov,set - Counters:
get_counter(feature "counter") - Distributed counters withincrement
Ephemeral State & Presence
- Not currently provided in the Rust crate. Model presence in your app layer alongside CRDT updates (e.g., via your network transport). Cursors can be shared using
get_cursordata if needed.
Version Control & History
- Undo/redo:
UndoManager- Local undo of user’s own edits - Time travel:
checkoutto anyFrontiers- Debug or review history - Version tracking:
oplog_vv,state_frontiers,VersionVector - Fork documents:
forkorfork_at- Create branches for experimentation - Merge branches:
import- Combine changes from forked documents
Performance & Storage
- Incremental updates:
export(ExportMode::updates(&their_vv))- Send only changes (seeExportMode::updates) - Compact history:
export(ExportMode::Snapshot)- Full state with compressed history (seeExportMode::Snapshot) - Shallow snapshots:
export(ExportMode::shallow_snapshot(&frontiers))- State without partial history (seeExportMode::shallow_snapshot)
Documentation
- Start with the Rust API docs for
LoroDoc(container management, versioning, import/export, events) That page hosts examples and details for most important methods you’ll use day-to-day. - Loro Website for more details and guides
- Loro Examples for more examples and guides
Getting Started
Add to your Cargo.toml:
[]
= "^1"
LoroDoc quick tour
- Containers:
get_text,get_map,get_list,get_movable_list,get_tree - Import/Export:
export(ExportMode::…),import,from_snapshot - Versioning:
oplog_vv,state_frontiers,checkout/checkout_to_latest,revert_to,fork - Events:
subscribe,subscribe_root,subscribe_local_update(send deltas to peers) - Paths/JSON:
get_path_to_container,get_deep_value/ToJson(to_json_value()), optionaljsonpath(feature)
Optional cargo features:
[]
= { = "^1", = ["jsonpath"] }
Quick Examples
- Local edits, change events, and two-peer sync
use ;
use Arc;
let a = new;
let b = new;
// Listen for container diffs on `a`
let _changes = a.subscribe_root;
a.get_text.insert.unwrap;
a.commit; // events fire on commit/export/import/checkout
// Sync via export/import (send `updates` via your transport)
let updates = a.export.unwrap;
b.import.unwrap;
assert_eq!;
- Time travel and revert
use LoroDoc;
let doc = new;
let text = doc.get_text;
text.insert.unwrap;
let v0 = doc.state_frontiers;
text.insert.unwrap;
assert_eq!;
// Time travel to v0 (read-only)
doc.checkout.unwrap;
assert_eq!;
// Return to latest and revert
doc.checkout_to_latest;
doc.revert_to.unwrap;
assert_eq!;