noteloom
← Back to blognoteloom 0.2.0: real-time collaboration, offline-first editing, and a two-line API

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:

ScenarioResult
Concurrent inserts, even at the same positionBoth survive, converging to the same order on every peer.
Concurrent delete vs. edit of the same blockThe delete wins.
Concurrent "Turn into" on the same blockOne type wins deterministically — the same one, on every peer.
Concurrent edits to the same run's textWhole-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 docs
noteloom.qusere.in/playground

A real, live noteloom instance embedded inside the article. Want the full thing? Open the Playground.