PolynomialHQdocs
SDKs

@polynomialhq/react

React providers and hooks for rooms, presence, storage, events, history, and connection state.

pnpm add @polynomialhq/react

React 18.2 or newer is required. The package depends on @polynomialhq/client and re-exports createClient.

Providers

PolynomialProvider

Makes a PolynomialClient available to descendant rooms:

const client = createClient({
  authEndpoint: "/api/polynomial-auth",
});

<PolynomialProvider client={client}>
  <App />
</PolynomialProvider>

PolynomialProviderProps accepts children and an optional client.

When client is omitted, createPolynomialClient() builds one with no authEndpoint and no publicApiKey. Such a client can still serve the options form of RoomProvider, where you supply the token yourself, but entering a room by id throws createClient requires authEndpoint, publicApiKey, or an enterRoom token.

Use usePolynomialClient() to read the current client. It throws outside a provider.

RoomProvider

Enter a client-managed room with the ID form:

<RoomProvider<Presence, Storage, Events>
  id="document:42"
  initialPresence={{ cursor: null }}
  initialStorage={{ blocks: [] }}
>
  <Editor />
</RoomProvider>

Or pass direct CreateRoomOptions:

<RoomProvider<Presence, Storage, Events>
  options={{
    initialPresence,
    initialStorage,
    roomId,
    token,
    url,
  }}
>
  <Editor />
</RoomProvider>

RoomProviderProps describes both forms. The provider connects on mount and leaves after unmount, with React Strict Mode replay handled internally.

The room is rebuilt when id, token, url, transport, or the reconnect settings change — which disconnects and reconnects. initialPresence and initialStorage are deliberately excluded, so a new object literal on each render does not churn the connection. Keep the other props stable.

useRoom

Returns Room<TPresence, TStorage, TBroadcastEvents> from the nearest room provider.

const room = useRoom<Presence, Storage, Events>();

Connection hooks

HookReturn value
useRoomSnapshot()Complete typed room snapshot
useConnectionSnapshot()Connection ID, user, room, role, status, and last error
useRoomStatus()Native RoomConnectionStatus
useStatus()Simplified five-state connection status
useStorageStatus()Native storage synchronization status
useSyncStatus(options?)"synchronized" | "synchronizing"

Every one of these is derived from useRoomSnapshot(), which subscribes to the room through useSyncExternalStore. A component using any room hook re-renders on every room event, including another user's cursor movement. Keep room-connected components small.

useStatus() maps the seven native statuses onto five:

useRoomStatus()useStatus()
idleinitial
connecting, connectedconnecting
joinedconnected
reconnectingreconnecting
closed, errordisconnected

UseSyncStatusOptions accepts smooth, which is currently accepted and ignored. SyncStatus is the exported result type.

Presence hooks

useMyPresence

const [presence, updateMyPresence] = useMyPresence<Presence>();

updateMyPresence({
  cursor: { x: 40, y: 80 },
});

The update function has type UpdateMyPresence<TPresence>. Its second parameter, UpdateMyPresenceOptions with addToHistory, is accepted for source compatibility and has no runtime effect — presence never enters the storage history.

usePresence() is the native equivalent. useUpdateMyPresence() returns only the updater.

initialPresence is not published on the first join. A connection becomes visible to others after its first updatePresence() call.

useOthers

Returns Array<RoomOther<TPresence>> for the other room connections, sorted by connectionId.

useSelf

Returns RoomSelf<TPresence, TUserInfo> | null. RoomSelf contains connectionId, id, info, and presence. It is null until the room reports both a connection ID and a user ID.

info is always an empty object in the current release. The userInfo claim on a room token is not yet delivered to the browser, so render identity from presence instead.

Storage hooks

useStorage

const blocks = useStorage<Presence, Storage, Events, Block[]>(
  (storage) => storage.blocks,
);

The type parameters are presence, storage, broadcast events, and the selector result. The optional selector maps the typed storage document to a selected value; without one, the whole document is returned.

useMutation

const addBlock = useMutation<Presence, Storage, [Block]>(
  ({ getPresence, setPresence, storage }, block) => {
    storage.updateAt<Block[]>(["blocks"], (blocks = []) => [...blocks, block]);
    setPresence({ selectedBlockId: block.id });
  },
  [],
);

The first argument receives RoomMutationContext; remaining arguments are defined by TArgs. Pass React dependencies as the second argument. All storage writes performed by one mutation call form a single undo entry.

History hooks

HookReturn value
useHistory()Imperative RoomHistory
useUndo()Stable undo callback
useRedo()Stable redo callback
useCanUndo()Current undo availability
useCanRedo()Current redo availability

RoomHistory exposes canUndo(), canRedo(), undo(), redo(), and clear(). There is no pause or resume.

Broadcast hooks

useBroadcast

Returns a typed (eventName, payload) callback:

const broadcast = useBroadcast<Events>();
broadcast("reaction", { emoji: "✨" });

useBroadcastEvent

With an event name and listener, subscribes to a native typed room event:

useBroadcastEvent<Events, "reaction">("reaction", ({ payload }) => {
  showReaction(payload);
});

Called with no arguments, it returns a sender for objects that carry their own type field.

The subscription is re-created whenever the listener identity changes, so memoize listeners that close over state. Events are delivered to the sender as well as to other connections.

useEventListener

Subscribes to every broadcast in the room and receives { connectionId, event, user }, where user is the matching entry from useOthers() or null for your own events.

Error and connection listeners

useErrorListener(listener) receives RoomConnectionError values. ErrorListener is the callback type.

useLostConnectionListener(listener) emits a LostConnectionEvent:

  • lost when reconnecting starts;
  • restored when the room rejoins;
  • failed when retrying ends in an error.

LostConnectionListener is the callback type. Both listeners are re-registered when their identity changes; pass memoized callbacks.

Client-side suspense

ClientSideSuspense renders fallback on the server and children in the browser:

<ClientSideSuspense fallback={<EditorSkeleton />}>
  {() => <Editor />}
</ClientSideSuspense>

ClientSideSuspenseProps describes children and fallback. It is a hydration guard rather than a data-loading boundary: it does not wait for storage. Gate on useStorageStatus() when the children need a loaded document.

The exported suspense object re-exports the same providers and hooks under one namespace for applications that import from a suspense entry point.

Client creation exports

  • createClient is re-exported from @polynomialhq/client.
  • createPolynomialClient() creates the default PolynomialClient.
  • PolynomialClient requires createRoom and optionally supports enterRoom and getRoom.
  • reactPackageName is the literal "@polynomialhq/react".

On this page