@polynomialhq/client
Framework-agnostic clients, rooms, connections, JSON storage, and Live structures.
pnpm add @polynomialhq/clientThis package is the framework-agnostic browser SDK. It owns authentication, room lifecycle, presence, broadcasts, durable storage, history, and the lower-level WebSocket connection.
createClient
function createClient(options?: CreateClientOptions): Client;import { createClient } from "@polynomialhq/client";
const client = createClient({
authEndpoint: "/api/polynomial-auth",
reconnect: true,
});CreateClientOptions
Prop
Type
AuthEndpoint can return a token string or AuthEndpointResult ({ token: string }).
WebSocketLikeConstructor and FetchLike describe the corresponding polyfills.
backgroundKeepAliveTimeout, lostConnectionTimeout, preventUnsavedChanges, and throttle
are accepted by the type for source compatibility. They have no runtime effect in this release.
Client
enterRoom
const { room, leave } = client.enterRoom<Presence, Storage, Events>(
"canvas:launch",
{
initialPresence: { cursor: null },
initialStorage: { blocks: [] },
},
);
await room.connect();EnterRoomOptions accepts initialPresence, initialStorage, and optional per-room token,
url, transport, and reconnect overrides. EnterRoomResult contains the typed room and a
leave() function that removes it from the client and disconnects it.
Entering the same room ID twice replaces the registry entry without disconnecting the first room.
Call leave() before re-entering.
getRoom
const room = client.getRoom<Presence, Storage, Events>("canvas:launch");Returns the entered room or null.
createRoom
const room = client.createRoom<Presence, Storage, Events>({
initialPresence,
initialStorage,
roomId: "canvas:launch",
token,
url: "wss://polynomialhq.live/v1/realtime",
});Creates a room directly from CreateRoomOptions. It bypasses the client's authEndpoint,
publicApiKey, url, and reconnect defaults — supply them yourself — and does not add the room
to the getRoom() registry.
Room
A Room<TPresence, TStorage, TBroadcastEvents> exposes:
| Member | Description |
|---|---|
connect() | Open the connection, join the room, and resolve the connection snapshot |
disconnect() | Close the room connection and drop its subscriptions |
getSnapshot() | Return the complete RoomSnapshot |
getConnectionSnapshot() | Return connection state |
getPresence() | Return local presence |
getOthers() | Return the other room connections |
updatePresence(patch) | Shallow-patch local presence |
broadcast(name, payload) | Send a typed transient event |
broadcastEvent(event) | Send an event object with a type field |
getStorage() | Return JsonDocumentStorage<TStorage> |
getStorageStatus() | Return the current RoomStorageStatus |
createMutation(fn) | Create a typed mutation function |
history | Access RoomHistory |
getHistorySnapshot() | Return RoomHistoryState |
subscribe(listener) | Listen for RoomEvent; returns an unsubscribe function |
disconnect() is terminal for the room object: it also unsubscribes the room from its connection
and storage, so a disconnected room cannot be reconnected. Create a new room instead.
Room state types
| Type | Shape |
|---|---|
RoomSnapshot | connection, history, others, presence, storage, storageStatus |
RoomOther | connectionId, userId, presence |
RoomBroadcastEvent | connectionId, typed event, typed payload |
RoomMutationContext | storage, getPresence(), setPresence() |
RoomHistoryState | canUndo, canRedo |
RoomStorageStatus | "loading" | "not-loaded" | "synchronized" | "synchronizing" |
RoomEvent is a discriminated union of connection.changed, error, others.changed,
presence.changed, broadcast, and storage.changed. Every variant carries the full snapshot
at the time of the event.
Standalone room APIs
createRoom(options) creates the same typed room without first creating a client:
import { createRoom } from "@polynomialhq/client";
const room = createRoom<Presence, Storage, Events>({
initialPresence,
initialStorage,
roomId,
token,
url,
});CreateRoomOptions combines the storage/presence seed with CreateRoomConnectionOptions and an
optional existing roomConnection. Passing roomConnection lets a test or adapter drive the room
with a connection it already controls.
Connection API
Use the connection layer when building an adapter or debugging the protocol.
import { createRoomConnection } from "@polynomialhq/client";
const connection = createRoomConnection({
reconnect: true,
roomId: "canvas:launch",
token,
url: "wss://polynomialhq.live/v1/realtime",
});
const snapshot = await connection.connect();RoomConnection
| Member | Description |
|---|---|
connect() | Connect and join |
disconnect() | Close the transport |
leave() | Send a room leave and stop reconnecting |
getSnapshot() | Return RoomConnectionSnapshot |
subscribe(listener) | Listen for RoomConnectionEvent |
updatePresence(presence) | Send raw presence |
broadcast(event, payload) | Send a raw broadcast |
send(message) | Send a typed client protocol message |
sendYjsUpdate(update) | Queue and send a base64 Yjs update |
getPendingYjsUpdateCount() | Read the outgoing update queue length |
ping(nonce?) | Send a protocol ping |
send(), leave(), ping(), broadcast(), and updatePresence() throw
Room connection is not connected. when no transport is open. sendYjsUpdate() is the exception:
it queues the update and flushes it after the next successful join.
RoomConnectionStatus is "idle" | "connecting" | "connected" | "joined" | "reconnecting" | "closed" | "error".
RoomConnectionSnapshot contains connectionId, lastError, role, roomId, status, and
userId. A RoomConnectionError contains an optional protocol code, a message, and
retryable.
RoomConnectionEvent covers status.changed, message, error, and yjs.outbox.changed.
RoomConnectionListener is the corresponding callback type.
Reconnection
RoomConnectionReconnectOptions accepts:
Prop
Type
Passing reconnect: false is equivalent to { enabled: false, maxAttempts: 0 }. The attempt
counter resets to zero on every successful join.
Custom transports
RoomConnectionTransportFactory receives onOpen, onMessage, onError, onClose, and url,
and returns a RoomConnectionTransport with send() and close().
Call onOpen() asynchronously. The connection layer sends room.join synchronously from the open
handler, and the transport reference is not assigned until your factory returns.
createWebSocketRoomConnectionTransport() is the built-in browser implementation.
RoomTokenProvider is () => string | Promise<string>.
JSON storage
import { createJsonDocumentStorage } from "@polynomialhq/client";
const storage = createJsonDocumentStorage<Storage>({
blocks: [],
title: "Untitled",
});JsonDocumentStorage provides getSnapshot, getAt, setAt, updateAt, replace, and
subscribe. A JsonDocumentChange contains next, previous, and the changed JsonPath.
JsonDocumentListener is its callback type.
Reads and writes both return deep clones, so no caller can hold a mutable reference into the document.
Storage types:
JsonPrimitive:boolean | null | number | stringJsonArray: an array ofJsonValueJsonObject: a string-keyed object ofJsonValueJsonValue:JsonPrimitive | JsonArray | JsonObjectJsonPathSegment:number | stringJsonPath: a readonly array of path segmentsStorageValue:JsonValue | Lson
Live structures
LiveObject
LiveObject<TData> supports get, set, update, delete, toObject, toImmutable, and
toJSON.
LiveMap
LiveMap<TKey, TValue> supports size, clear, delete, entries, forEach, get, has,
keys, set, toObject, toImmutable, toJSON, values, and iteration.
LiveList
LiveList<TValue> supports length, clear, delete, forEach, get, insert, map,
move, push, set, toArray, toImmutable, toJSON, and iteration.
Lson describes JSON plus nested LiveStructure values. normalizeLsonValue() converts Lson
or JSON to plain JsonValue.
These structures are builders for initial documents. A room normalizes them to plain JSON on creation and never hands one back, so mutating a retained instance does not change room state.
Other exports
clientPackageName is the literal "@polynomialhq/client". It is primarily useful for package
smoke tests and diagnostics.