PolynomialHQdocs
SDKs

@polynomialhq/client

Framework-agnostic clients, rooms, connections, JSON storage, and Live structures.

pnpm add @polynomialhq/client

This 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:

MemberDescription
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
historyAccess 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

TypeShape
RoomSnapshotconnection, history, others, presence, storage, storageStatus
RoomOtherconnectionId, userId, presence
RoomBroadcastEventconnectionId, typed event, typed payload
RoomMutationContextstorage, getPresence(), setPresence()
RoomHistoryStatecanUndo, 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

MemberDescription
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 | string
  • JsonArray: an array of JsonValue
  • JsonObject: a string-keyed object of JsonValue
  • JsonValue: JsonPrimitive | JsonArray | JsonObject
  • JsonPathSegment: number | string
  • JsonPath: a readonly array of path segments
  • StorageValue: 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.

On this page