Skip to content
Roomful is in public beta — install with the @beta tag. Share feedback →

React Adapter (`@roomful/react`)

Audience: users.

import { RoomfulProvider } from '@roomful/react';
function App() {
return (
<RoomfulProvider
roomId="my-room"
transport="auto"
presence={{ name: 'Alice', color: '#4F46E5' }}
onConnect={() => console.log('connected')}
onError={(error) => console.error(error)}
>
<Workspace />
</RoomfulProvider>
);
}
HookReturnsPurpose
useRoom()Roomaccess low-level room instance
usePresence(){ self, others, all }reactive participant data
useCursors(){ ref, cursors, mount, unmount }cursor tracking/rendering
useSharedState(key, opts)[value, setValue]synchronized state
useAwareness(){ set, setFocus, setSelection, setTyping, others }ephemeral peer context
useEvent(name, handler)emit functionsubscribe and emit
usePeers()Peer[]connected peers
useConnectionStatus()RoomStatuscurrent room status

useSharedState(key, opts) intentionally mirrors React useState: it returns a [value, setValue] tuple, and setValue accepts either the next value or an updater function.

import { useCursors, useSharedState } from '@roomful/react';
function PollWidget() {
const { ref, cursors } = useCursors<{ tool: 'pen' | 'eraser' }>();
const [votes, setVotes] = useSharedState('poll-votes', {
initialValue: { yes: 0, no: 0 },
strategy: 'crdt',
});
return (
<div ref={ref}>
<p>
Yes: {votes.yes} | No: {votes.no}
</p>
<p>Remote cursors: {cursors.length}</p>
<button onClick={() => setVotes((v) => ({ ...v, yes: v.yes + 1 }))}>Vote Yes</button>
</div>
);
}
  • useSharedState() currently binds one shared-state engine per room. Every component in that room must use the same key.
  • opts forwards directly to room.useState(...), including initialValue, strategy, and persist.
  • The setter reference is stable across rerenders and room replacement.