diff --git a/apps/web/app/(docs)/docs/extensions/CollaborationExtension/codes.tsx b/apps/web/app/(docs)/docs/extensions/CollaborationExtension/codes.tsx
new file mode 100644
index 0000000..e62f322
--- /dev/null
+++ b/apps/web/app/(docs)/docs/extensions/CollaborationExtension/codes.tsx
@@ -0,0 +1,143 @@
+import { RegisteredCodeSnippet } from "../../../lib/types";
+
+export const COLLABORATION_EXTENSION_CODES: RegisteredCodeSnippet[] = [
+ {
+ id: "collaboration-install",
+ code: `# Pick a transport: y-webrtc (P2P), y-websocket (server), or y-indexeddb (offline)
+pnpm add yjs y-webrtc`,
+ language: "bash",
+ title: "Install",
+ description:
+ "yjs + a transport are optional peer deps — an editor without the extension pulls in nothing collaboration-related.",
+ },
+ {
+ id: "collaboration-quick-start",
+ code: `import {
+ createEditorSystem,
+ richTextExtension,
+ collaborationExtension,
+ type CollaborationProvider,
+} from "@lexkit/editor";
+import { WebrtcProvider } from "y-webrtc";
+import * as Y from "yjs";
+
+const extensions = [
+ richTextExtension,
+ collaborationExtension.configure({
+ id: "my-room",
+ username: "Ada",
+ cursorColor: "#e11d48",
+ providerFactory: (id, docMap) => {
+ let doc = docMap.get(id);
+ if (!doc) { doc = new Y.Doc(); docMap.set(id, doc); }
+ return new WebrtcProvider(id, doc) as unknown as CollaborationProvider;
+ },
+ }),
+] as const;
+
+const { Provider, useEditor } = createEditorSystem
-
- {children}
-
The extension ships with LexKit. You add yjs and a
- transport of your choice (they are optional peer dependencies — an
- editor without the extension pulls in nothing collaboration-related).
+ transport of your choice — they are optional peer dependencies.
{`# pick a transport: y-webrtc (P2P), y-websocket (server), or y-indexeddb (offline)
-pnpm add yjs y-webrtc`}
+
- Collaboration is just another extension. The only required option is{" "}
- providerFactory — the single seam where you plug in a
- transport. LexKit never bundles one.
-
{`import {
- createEditorSystem,
- richTextExtension,
- collaborationExtension,
- type CollaborationProvider,
-} from "@lexkit/editor";
-import { WebrtcProvider } from "y-webrtc";
-import * as Y from "yjs";
-
-const extensions = [
- richTextExtension,
- collaborationExtension.configure({
- id: "my-room",
- username: "Ada",
- cursorColor: "#e11d48",
- providerFactory: (id, docMap) => {
- let doc = docMap.get(id);
- if (!doc) { doc = new Y.Doc(); docMap.set(id, doc); }
- return new WebrtcProvider(id, doc) as unknown as CollaborationProvider;
- },
- }),
-] as const;
-
-const { Provider, useEditor } = createEditorSystem();`}
-
- The extension stays inert until providerFactory is set, so
- it's safe to leave in the array and toggle collaboration on later.
+ The only required option is providerFactory — the single
+ seam where you plug in a transport. LexKit never bundles one, and the
+ extension stays inert until it's set.
Provider interface works.
- {`import { WebsocketProvider } from "y-websocket";
-
-providerFactory: (id, docMap) => {
- let doc = docMap.get(id);
- if (!doc) { doc = new Y.Doc(); docMap.set(id, doc); }
- return new WebsocketProvider("wss://your-server", id, doc) as any;
-}`}
- {`import { WebrtcProvider } from "y-webrtc";
-
-providerFactory: (id, docMap) => {
- let doc = docMap.get(id);
- if (!doc) { doc = new Y.Doc(); docMap.set(id, doc); }
- return new WebrtcProvider(id, doc, {
- signaling: ["wss://your-signaling-server"],
- }) as any;
-}`}
- {`import { IndexeddbPersistence } from "y-indexeddb";
-
-providerFactory: (id, docMap) => {
- let doc = docMap.get(id);
- if (!doc) { doc = new Y.Doc(); docMap.set(id, doc); }
- // Persist locally so edits survive reloads and replay when back online.
- new IndexeddbPersistence(id, doc);
- return new WebsocketProvider("wss://your-server", id, doc) as any;
-}`}
+
- Build any presence UI with the headless useCollaborators(){" "}
- hook, or drive it imperatively through{" "}
+ Build any presence UI with the headless{" "}
+ useCollaborators() hook, or drive it imperatively through{" "}
commands.collab.
{`import { useCollaborators } from "@lexkit/editor";
-
-function PresenceBar() {
- const { users, isConnected } = useCollaborators();
- return (
-
- {isConnected ? "live" : "offline"} · {users.length} online
- {users.map((u) => (
-
- {u.name}{u.isLocal ? " (you)" : ""}
-
- ))}
-
- );
-}`}
- {`const { commands, activeStates } = useEditor();
-
-commands.collab.connect();
-commands.collab.disconnect();
-commands.collab.toggle();
-commands.collab.setLocalUser({ name: "Ada", color: "#e11d48", awarenessData: { avatar } });
-commands.collab.getUsers(); // CollaboratorState[]
-commands.collab.onUsersChange(cb); // subscribe; returns unsubscribe
-activeStates.isCollaborating; // boolean`}
+ cursorColor. Supply your own{" "}
cursorsContainerRef to portal carets anywhere.
- {`const theme = {
- collab: {
- cursorsContainer: "my-cursors-layer",
- caret: "my-collab-caret",
- label: "my-collab-label",
- selection: "my-collab-selection",
- },
-};`}
+
Yjs is a CRDT: when a client drops offline and keeps editing, then
reconnects, all edits merge automatically with no data loss and every
- replica converges to the same state. Add{" "}
- y-indexeddb to persist a client's edits locally so
- they survive reloads and replay on reconnect. This behaviour is
- identical no matter how content is imported (Markdown, HTML or JSON) —
- collaboration syncs the editor state itself.
+ replica converges to the same state. Add y-indexeddb to
+ persist a client's edits locally so they survive reloads and
+ replay on reconnect. This behaviour is identical no matter how content
+ is imported (Markdown, HTML or JSON) — collaboration syncs the editor
+ state itself.