
23 July 2026
noteloom 0.2.0: real-time collaboration, offline-first editing, and a two-line API
noteloom started as a zero-dependency block editor that got the small things right — inline widgets, undo/redo, a normalized document model. 0.2.0 is the release where it grows up into something you can actually ship a real product on: peers editing the same document live, documents that keep working with no network at all, and a setup path that no longer requires understanding the whole engine before you can render a paragraph.
What's new, in one breath
Four changes, and they compose with everything that shipped before them:
- useEditor() + <NoteloomEditor> — a fully wired editor in two lines, no engine internals required to get started
- Real-time collaboration (CollabSession) — multi-peer editing over WebRTC via a custom block-tree CRDT, bring your own signaling
- Offline persistence (usePersistedDocument) — auto-save to IndexedDB, reload with zero network required
- Opt-in tombstone garbage collection — keeps a long-running collaborative session from growing memory unbounded
A two-line editor
Every earlier release exposed the full granular API first — a store, two registries, a provider, a fistful of hooks — because that's genuinely what the engine is built from. 0.2.0 adds a batteries-included entry point on top of it, the same relationship TipTap's useEditor() has to ProseMirror underneath:
import { useEditor, NoteloomEditor } from 'noteloom';
function Editor() {
const editor = useEditor();
return <NoteloomEditor editor={editor} />;
}That's undo/redo, every built-in block and inline type, clipboard, slash/@/emoji menus, and the floating format toolbar — all pre-wired, zero CSS imports. Nothing was removed to make room for it: editor.store/registry/inlineRegistry are the exact same objects the granular API (EditorProvider, BlockChildren, individual hooks) already expects, so dropping down for a custom toolbar or mobile chrome is never a rewrite, just an addition.
Real-time collaboration, no server required
This is the headline feature: two or more people editing the same noteloom document at once, changes merging live. It's built as a custom block-tree CRDT — not a generic text-CRDT library wired in from outside — specifically so the whole thing stays true to noteloom's zero-runtime-dependency design. Peers connect directly over WebRTC; you bring your own signaling (a WebSocket relay, Firebase/Supabase realtime, anything that can pass small JSON messages) just to help two peers find each other:
import { useEditor, NoteloomEditor, CollabSession } from 'noteloom';
import { useEffect } from 'react';
function App() {
const editor = useEditor({ doc: myDoc });
useEffect(() => {
const session = new CollabSession({ history: editor.store, signaling });
session.connect(remotePeerId, { initiator: true });
return () => session.destroy();
}, []);
return <NoteloomEditor editor={editor} />;
}From there, every edit — typing, inserting/moving/deleting blocks, even a 'Turn into' type conversion — broadcasts automatically, and incoming changes from every connected peer merge straight into the same store your UI already renders. For getting a WebSocket relay running on your own LAN in minutes, or wiring the ready-made presence hook for live cursors, see the full collaboration guide linked below.
Presence: live cursors, who’s online
Alongside the document CRDT, CollabSession relays a second, ephemeral channel — usePresence(session) gives you a live Map<peerId, data> of whatever each peer chose to broadcast (a cursor position, a name, a color). It’s never persisted and never merge-conflicted; a peer’s entry simply disappears the instant they disconnect.
How conflicts actually resolve
Worth knowing exactly what happens before two people hit the same block at once:
| Scenario | Result |
|---|---|
| Concurrent inserts, even at the same position | Both survive, converging to the same order on every peer. |
| Concurrent delete vs. edit of the same block | The delete wins. |
| Concurrent "Turn into" on the same block | One type wins deterministically — the same one, on every peer. |
| Concurrent edits to the same run's text | Whole-value last-write-wins; character-level interleaving is not implemented yet. |
Works offline, saves itself
The second half of shipping something real: a document that keeps working with the network off, and picks up exactly where you left it. usePersistedDocument wires a store to IndexedDB — native to the browser, no dependency added:
import { useEditor, NoteloomEditor, usePersistedDocument } from 'noteloom';
function App() {
const editor = useEditor({ doc: myStarterDoc });
const { isLoaded } = usePersistedDocument({ store: editor.store, docId: 'my-document-id' });
if (!isLoaded) return <p>Loading…</p>;
return <NoteloomEditor editor={editor} />;
}Every edit auto-saves back, debounced so typing doesn't trigger a full-document IndexedDB write per keystroke. It composes directly with collaboration too — wire both to the same store, and a document keeps saving locally even after every collaborating peer disconnects. Pair it with a standard PWA build (vite-plugin-pwa, say) plus the exported useServiceWorkerUpdate() hook, and the whole app — not just the document — loads with zero network.
Both collaboration and offline persistence are new in 0.2.0 and explicitly marked experimental in the docs — read the known-limitations section before relying on either in production, especially around undo interacting with a peer's concurrent edits.
What to know before you ship it
Straight from the docs, not buried in an issue tracker:
- Undo is local-only — undoing your own past edit to a run can overwrite a peer's newer edit to that same run, since text merges as whole-value last-write-wins.
- A peer joining with their own different document does not merge with yours — CollabSession only adopts a shared document when your side starts empty.
- Reconnecting after a drop re-syncs the whole document, not just what was missed — simple and correct, at the cost of more traffic per reconnect.
Try it
Both features are fully documented with runnable examples — a same-browser BroadcastChannel demo needing zero server setup, a LAN relay for real multi-device testing, and a working offline notes app pattern. Start here:
Read the Live collaboration docsRead the Offline persistence docsA real, live noteloom instance embedded inside the article. Want the full thing? Open the Playground.