Client hooks
The React package — the provider and its config, the hooks and which one to reach for, why the client never hard-codes an engine, and how sessions are reference counted.
@adonis-agora/collaboration-client is the React half. It is engine-agnostic on purpose: it
asks the server which engine a document uses and picks the matching transport, so a component that
edits researches/42/writing does not change when that document moves from the embedded Yjs server
to a worker at the edge.
pnpm add @adonis-agora/collaboration-clientThe provider
Everything runs under one provider, mounted once near the root:
import { CollaborationProvider } from '@adonis-agora/collaboration-client'
<CollaborationProvider baseUrl={window.location.origin}>
<App />
</CollaborationProvider>| Prop | Purpose |
|---|---|
baseUrl | your Adonis origin. Optional — falls back to location.origin, so a same-origin app can leave it out entirely |
getHeaders | called per request — return the auth headers your API needs. A function, so a rotating token is picked up without a remount |
createTransport | override the transport factory — tests, or an engine of your own |
fetchImpl | override fetch — tests and SSR |
The provider's real job is holding the session map: one DocSession per document name, each
owning a Y.Doc, a transport and a reconnect loop. Sessions are created on demand and
reference counted — the socket opens with the first hook that mounts and closes when the last
one unmounts, while the Y.Doc stays in the map, so navigating back resumes on the document you
left instead of starting from an empty one. See Sessions.
Mount it above the router, not inside a page
A provider that remounts on navigation throws its whole session map away — documents included — so every route change starts from scratch. Put it in the layout.
The hooks
| Hook | Returns | Backed by |
|---|---|---|
useCollabDoc | the shared Y.Doc (and its collabDoc view) plus status, synced and error | WebSocket |
useAutomergeDoc | the Automerge document plus change(fn), synced and pendingChanges | WebSocket |
useCollabEditor | an editor-agnostic { state, update } view, plus synced and peers | WebSocket |
useAwareness | peers — everyone else in the room — plus setLocalState to publish yourself | WebSocket |
useExcalidrawSync | nothing — it wires a Y.Doc (or a collabDoc) to Excalidraw's imperative API | WebSocket |
useComments | comments, create, resolve, remove, refresh | REST |
useVersions | versions, create, restore, refresh | REST |
Two things follow from that last column. The REST-backed hooks keep working while the socket is
down — they open no session at all, so a page with no editor on it can list a document's comments
— and they are not live: new comments appear on refresh(), not the instant someone else posts
one.
Picking a document hook
Is the server's engine for this document 'automerge'?
├── yes → useAutomergeDoc
└── no → is there an editor adapter for your surface?
├── yes (Excalidraw, tldraw, plain text) → useCollabEditor
└── no (Tiptap, CodeMirror, custom) → useCollabDoc, bind the Y.Doc yourselfuseCollabDoc gives you the raw Y.Doc and gets out of the way — the right choice for Tiptap,
where the editor's own extension does the binding. useCollabEditor is a level up: an adapter maps
a whole scene in and out of one document key, so the component only ever sees
{ state, update }. See Editors.
useCollabDoc fails on an Automerge document
An Automerge document cannot back a Y.Doc, so there is no honest way to return one. Pointing
useCollabDoc at a document the server resolves to automerge surfaces a clear message on the
hook's error — it does not throw out of the render, so check error, do not wrap the hook — rather
than handing back an empty document that never syncs. See
Automerge.
Below the hooks
Every hook is built on exported pieces you can use directly when a hook does not fit — a non-React frontend, a script, a test:
DocSession— the token fetch, transport and reconnect loop for one document, plus theretain/release/stop/destroylifecycle the hooks drive for you.fetchToken,listComments,createComment,resolveComment,deleteComment,listVersions,createVersion,restoreVersion— the REST client, plusCollabRestError.HocuspocusCollabTransport,PartyKitCollabTransport,PartyServerCollabTransportanddefaultTransportFactory— the transports and the factory that chooses between them.
Typed registry
The generated .adonisjs/collaboration/documents.ts — how the union is derived from disk, when it refreshes, what it looks like when empty, and how to use it on both sides.
Sessions
One Y.Doc and one transport per document name — how a session starts, what each status means, how reconnection with fresh tokens works, and why the doc reference is stable across renders.