#crdt #collaborative-editing #local-first

loro

Loro is a high-performance CRDTs framework. Make your app collaborative efforlessly.

35 releases (22 stable)

new 1.10.3 Dec 9, 2025
1.10.0 Nov 27, 2025
1.8.1 Sep 23, 2025
1.5.10 Jul 22, 2025
0.1.0 Aug 7, 2022

#65 in Network programming

Download history 232/week @ 2025-09-14 598/week @ 2025-09-21 216/week @ 2025-09-28 228/week @ 2025-10-05 163/week @ 2025-10-12 460/week @ 2025-10-19 121/week @ 2025-10-26 101/week @ 2025-11-02 172/week @ 2025-11-09 192/week @ 2025-11-16 313/week @ 2025-11-23 798/week @ 2025-11-30 1417/week @ 2025-12-07

2,731 downloads per month
Used in 5 crates (4 directly)

MIT license

2.5MB
59K SLoC

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

Real-time Collaboration

Rich Text Editing

Data Structures

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_cursor data if needed.

Version Control & History

Performance & Storage

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:

[dependencies]
loro = "^1"

LoroDoc quick tour

Optional cargo features:

[dependencies]
loro = { version = "^1", features = ["jsonpath"] }

Quick Examples

  1. Local edits, change events, and two-peer sync
use loro::{LoroDoc, ExportMode};
use std::sync::Arc;

let a = LoroDoc::new();
let b = LoroDoc::new();

// Listen for container diffs on `a`
let _changes = a.subscribe_root(Arc::new(|e| {
    println!("changed containers: {}", e.events.len());
}));

a.get_text("text").insert(0, "Hello, Loro!").unwrap();
a.commit(); // events fire on commit/export/import/checkout

// Sync via export/import (send `updates` via your transport)
let updates = a.export(ExportMode::all_updates()).unwrap();
b.import(&updates).unwrap();

assert_eq!(a.get_deep_value(), b.get_deep_value());
  1. Time travel and revert
use loro::LoroDoc;

let doc = LoroDoc::new();
let text = doc.get_text("text");
text.insert(0, "Hello").unwrap();
let v0 = doc.state_frontiers();

text.insert(5, ", world").unwrap();
assert_eq!(text.to_string(), "Hello, world");

// Time travel to v0 (read-only)
doc.checkout(&v0).unwrap();
assert_eq!(text.to_string(), "Hello");

// Return to latest and revert
doc.checkout_to_latest();
doc.revert_to(&v0).unwrap();
assert_eq!(text.to_string(), "Hello");

Dependencies

~9–29MB
~510K SLoC