@adonis-agora/collaboration-client
v0.8.1
Published
Client-side React hooks for @adonis-agora/collaboration — collaborative docs, presence, comments and versions. Part of the Agora ecosystem.
Maintainers
Readme
@adonis-agora/collaboration-client
React client hooks for @adonis-agora/collaboration — collaborative documents, presence, anchored comments and version control with automatic transport selection (Hocuspocus self-hosted or PartyKit edge).
React 18/19 · zero dependencies beyond yjs stack · ESM.
Install
pnpm add @adonis-agora/collaboration-clientServer side: install @adonis-agora/collaboration and run node ace collaboration:init to publish the REST routes these hooks consume.
Usage
Wrap your app once:
import { CollaborationProvider } from '@adonis-agora/collaboration-client'
<CollaborationProvider
baseUrl="https://api.app"
getHeaders={() => ({ cookie: sessionCookie })}
>
<App />
</CollaborationProvider>Then per collaborative view:
import {
useCollabDoc,
useAwareness,
useComments,
useVersions,
} from '@adonis-agora/collaboration-client'
function Writing({ docName }: { docName: string }) {
// Y.Doc shared per docName (cached across mounts), + connection status
const { doc, status, error } = useCollabDoc(docName)
const { peers } = useAwareness(docName) // who's online
const { comments, create, resolve, remove } = useComments(docName, 'text')
const { versions, create: createVersion, restore } = useVersions(docName)
if (status === 'error') return <p>{error?.message}</p>
return (
<>
{/* feed `doc` into Tiptap / ProseMirror / Monaco… */}
{peers.map((peer) => <Avatar key={peer.clientId} name={peer.name} />)}
</>
)
}How it works
useCollabDocfetches an ephemeral token fromGET {baseUrl}/collaboration/token?doc=<name>(your auth middleware protects this route).- The response's
enginefield picks the transport:yjs→HocuspocusProvideragainst the Adonis server (wsUrlpath)partykit→YPartyKitProviderstraight to the Cloudflare room
- Comments and versions are REST, not CRDT state — they live in your database via the server package's storage.
- Sessions survive component unmounts; one
Y.Docper document name per provider.
Custom transport
Bring your own sync engine by injecting a factory:
<CollaborationProvider baseUrl="…" createTransport={(info, doc, docName) => new MyTransport(info, doc)}>API
| Hook | Returns |
|---|---|
| useCollabDoc(docName) | { doc: Y.Doc, status: 'connecting'\|'connected'\|'disconnected'\|'error', error } |
| useAwareness(docName) | { peers: CollabPeer[], status } |
| useComments(docName, space?) | { comments, loading, error, refresh, create, resolve, remove } |
| useVersions(docName) | { versions, loading, error, refresh, create, restore } |
License
MIT
