API Reference - @liveblocks/client

createClient

Create a client that will be responsible to communicate with Liveblocks servers.

createClient with public key

When creating a client with a public key, you don’t need to set up an authorization endpoint:

import { createClient } from "@liveblocks/client";
const client = createClient({ publicApiKey: "pk_prod_xxxxxxxxxxxxxxxxxxxxxxxx",});

createClient with auth endpoint

If you are not using a public key, you need to set up your own authEndpoint. Please refer to our Authentication guide.

import { createClient } from "@liveblocks/client";
const client = createClient({ authEndpoint: "/api/auth" });

createClient with callback

If you need to add additional headers or use your own function to call the endpoint, authEndpoint also supports a callback.

import { createClient } from "@liveblocks/client";
const client = createClient({ authEndpoint: async (room) => { const response = await fetch("/api/auth", { method: "POST", headers: { Authentication: "token", "Content-Type": "application/json", }, body: JSON.stringify({ room }), }); return await response.json(); },});

createClient for React Native

If you want to use @liveblocks/client with React Native, you need to add an atob polyfill.

As a polyfill, we recommend installing the package base-64.

$npm install base-64

Then you can pass the decode function to our atob polyfill option when you create the client.

import { createClient } from "@liveblocks/client";import { decode } from "base-64";
const client = createClient({ /* ... other options ... */ polyfills: { atob: decode, },});

createClient for Node.js

If you want to use @liveblocks/client in a Node.js environment, you need to provide WebSocket and fetch polyfills.

As polyfills, we recommend installing the packages ws and node-fetch.

$npm install ws node-fetch

Then, pass them to the createClient function as below.

import { createClient } from "@liveblocks/client";import fetch from "node-fetch";import WebSocket from "ws";
const client = createClient({ /* ... other options ... */ polyfills: { fetch, WebSocket, },});

Note that node-fetch v3+ does not support CommonJS. If you are using CommonJS, downgrade node-fetch to v2.

WebSocket throttle

By default, the client throttles the WebSocket messages sent to 100 milliseconds.

It is possible to override that configuration with the throttle option.

throttle should be between 16 and 1000 milliseconds.

import { createClient } from "@liveblocks/client";
const client = createClient({ /* ... other options ... */ throttle: 16,});

Lost connection timeout

If you’re connected to a room and you briefly lose connection, Liveblocks will normally reconnect automatically and quickly. However, if this takes longer than usual (for example, your network is offline), then the room will emit an event informing you about such exceptional circumstances.

How quickly this happens can be configured with the lostConnectionTimeout setting.

lostConnectionTimeout can be set between 1000 (pedantic) and 30000 (very relaxed) milliseconds. The default is 5000, or 5 seconds.

import { createClient } from "@liveblocks/client";
const client = createClient({ /* ... other options ... */ lostConnectionTimeout: 5000,});

You can listen to the event with room.subscribe("lost-connection"). Note that this also affects when others are reset to an empty array after a disconnection. For a demonstration of this behavior, see our connection status example.

Client

Client returned by createClient.

Client.enter

Enters a room and returns it. The authentication endpoint is called as soon as you call this function.

The second argument is a configuration for the presence and storage.

  • initialPresence - The initial Presence to use for the User currently entering the Room. Presence data belongs to the current User and is readable to all other Users in the room while the current User is connected to the Room. Must be serializable to JSON.
  • initialStorage (optional) - The initial Storage structure to create when a new Room is entered for the first time. Storage data is shared and belongs to the Room itself. It persists even after all Users leave the room, and is mutable by every client. Must either contain Live structures (e.g. new LiveList(), new LiveObject({ a: 1 }), etc.) or be serializable to JSON.
  • shouldInitiallyConnect (optional) - Whether or not the room connects to Liveblocks servers. Default is true. Usually set to false when the client is used from the server to not call the authentication endpoint or connect via WebSocket.
const room = client.enter("my-room", {  initialPresence: { cursor: null },  initialStorage: { todos: new LiveList() },});

Client.leave

Leaves a room.

Client.getRoom

Gets a room by id. Returns null if client.enter has not been called previously.

const room = client.getRoom("my-room");

Room

Room returned by client.enter or client.getRoom.

Room.getPresence

Gets the presence of the current user.

const presence = room.getPresence();

Room.updatePresence

Updates the presence of the current user. Only pass the properties you want to update. No need to send the full presence.

room.updatePresence({ x: 0 });room.updatePresence({ y: 0 });
const presence = room.getPresence();// presence is equivalent to { x: 0, y: 0 }

updatePresence accepts an optional argument to add a new item to the undo/redo stack. See room.history for more information.

room.updatePresence({ selectedId: "xxx" }, { addToHistory: true });room.updatePresence({ selectedId: "yyy" }, { addToHistory: true });room.history.undo();// room.getPresence() equals { selectedId: "xxx" }

Room.getOthers

Gets all the other users in the Room.

const others = room.getOthers();
for (const { connectionId, id, info, presence, isReadOnly } of others) { // Do things}

Room.broadcastEvent

Broadcast an event to other users in the Room. Events broadcast to the room can be listened to with Room.subscribe("event"). Takes a payload as first argument. Should be serializable to JSON.

By default, broadcasting an event acts as a “fire and forget”. If the user is not currently in the room, the event is simply discarded. Passing the shouldQueueEventIfNotReady option will queue the event, before sending it once the connection is established.

// On client Aroom.broadcastEvent({ type: "EMOJI", emoji: "🔥" });
// On client Broom.subscribe("event", ({ event }) => { if (event.type === "EMOJI") { // Do something }});

Room.getSelf

Gets the current user. Returns null if it is not yet connected to the room.

const { connectionId, presence, id, info, isReadOnly } = room.getSelf();

Room.getStatus

Gets the current WebSocket connection status of the room.

const status = room.getStatus();

The possible value are: initial, connecting, connected, reconnecting, or disconnected.

Room.subscribe(storageItem)

Subscribe to updates for a particular storage item.

Takes a callback that is called when the storage item is updated.

Returns an unsubscribe function.

const { root } = await room.getStorage();const unsubscribe = room.subscribe(root, (root) => {  // Do something});

It’s also possible to subscribe to a storage item and its children by passing an optional isDeep parameter. In that case, the callback will get called with a list of updates instead. Each such update is a { type, node, updates } object.

const { root } = await room.getStorage();const unsubscribe = room.subscribe(  root,  (storageUpdates) => {    for (const update of storageUpdates) {      const {        type, // "LiveObject", "LiveList", or "LiveMap"        node,        updates,      } = update;      switch (type) {        case "LiveObject": {          // updates["property"]?.type; is "update" or "delete"          // update.node is the LiveObject that has been updated/deleted          break;        }        case "LiveMap": {          // updates["key"]?.type; is "update" or "delete"          // update.node is the LiveMap that has been updated/deleted          break;        }        case "LiveList": {          // updates[0]?.type; is "delete", "insert", "move", or "set"          // update.node is the LiveList that has been updated, deleted, or modified          break;        }      }    }  },  { isDeep: true });

Room.subscribe("event")

Subscribe to events broadcasted by Room.broadcastEvent.

Takes a callback that is called when a user calls Room.broadcastEvent.

Returns an unsubscribe function.

const unsubscribe = room.subscribe("event", ({ event, connectionId }) => {  // Do something});

Room.subscribe("my-presence")

Subscribe to the current user presence updates.

Takes a callback that is called every time the current user presence is updated with Room.updatePresence.

Returns an unsubscribe function.

const unsubscribe = room.subscribe("my-presence", (presence) => {  // Do something});

Room.subscribe("others")

Subscribe to the other users updates.

Takes a callback that is called when a user enters or leaves the room or when a user update its presence.

Returns an unsubscribe function.

const unsubscribe = room.subscribe("others", (others, event) => {  // Do something
if (event.type === "leave") { // A user has left the room // event.user; }
if (event.type === "enter") { // A user has entered the room // event.user; }
if (event.type === "update") { // A user has updated // event.user; // event.updates; }
if (event.type === "reset") { // A disconnection has occurred and others has reset});

Room.subscribe("status")

Subscribe to WebSocket connection status updates.

Takes a callback that is called whenever the connection status changes. Possible value are: initial, connecting, connected, reconnecting, or disconnected.

Returns an unsubscribe function.

const unsubscribe = room.subscribe("status", (status) => {  // Do something});

This is a low-level API that exposes the WebSocket’s connectivity status. You can use this, for example, to update a connection status indicator in your UI. It would be normal for a client to briefly lose the connection and restore it, using quick connectedreconnectingconnected status jumps.

If you want to let users know that there may be connectivity issues, don’t use this API, but instead refer to Room.subscribe("lost-connection").

Room.subscribe("lost-connection")

A special-purpose event that will fire in the exceptional case where a previously connected Liveblocks client has lost its connection (e.g. due to a network outage) and was unable to recover quickly.

This event allows you to build high-quality UIs by warning your users that the app is still trying to re-establish the connection, for example through a toast notification. You may want to take extra care in the mean time to ensure their changes won’t go unsaved, or to help them understand why they’re not seeing updates made by others yet.

When this happens, this callback is called with the event lost. Then, once the connection restores, the callback will be called with the value restored. If the connection could definitively not be restored, it will be called with failed (uncommon).

The lostConnectionTimeout configuration option will determine how quickly this event will fire after a connection loss (default: 5 seconds).

import { toast } from "my-preferred-toast-library";
const unsubscribe = room.subscribe("lost-connection", (event) => { switch (event) { case "lost": toast.warn("Still trying to reconnect..."); break;
case "restored": toast.success("Successfully reconnected again!"); break;
case "failed": toast.error("Could not restore the connection"); break; }});

Room.subscribe("connection")

Subscribe to our legacy WebSocket connection status updates.

Takes a callback that is called when the connection status changes.

Returns an unsubscribe function.

const unsubscribe = room.subscribe("connection", (status) => {  // Do something});

Room.subscribe("error")

Subscribe to potential room connection errors.

Returns an unsubscribe function.

const unsubscribe = room.subscribe("error", (error) => {  if (error.code === 4005) {    // Maximum concurrent connections per room exceeded.  }});

Room.subscribe("history")

Subscribe to the current user’s history changes.

Returns an unsubscribe function.

const unsubscribe = room.subscribe("history", ({ canUndo, canRedo }) => {  // Do something});

Room.subscribe("storage-status")

Subscribe to storage status changes.

Returns an unsubscribe function.

const unsubscribe = room.subscribe("storage-status", (status) => {  switch (status) {    case "not-loaded":      break;    case "loading":      break;    case "synchronizing":      break;    case "synchronized":      break;    default:      break;  }});

Room.batch

Batches modifications made during the given function.

All the modifications are sent to other clients in a single message.

All the subscribers are called only after the batch is over.

All the modifications are merged in a single history item (undo/redo).

const { root } = await room.getStorage();room.batch(() => {  root.set("x", 0);  room.updatePresence({ cursor: { x: 100, y: 100 } });});

Room.history

Room’s history contains functions that let you undo and redo operation made on by the current client on the presence and storage.

const { undo, redo, pause, resume } = room.history;

Room.history.undo

Undoes the last operation executed by the current client. It does not impact operations made by other clients.

room.updatePresence({ selectedId: "xxx" }, { addToHistory: true });room.updatePresence({ selectedId: "yyy" }, { addToHistory: true });room.history.undo();// room.getPresence() equals { selectedId: "xxx" }

Room.history.redo

Redoes the last operation executed by the current client. It does not impact operations made by other clients.

room.updatePresence({ selectedId: "xxx" }, { addToHistory: true });room.updatePresence({ selectedId: "yyy" }, { addToHistory: true });room.history.undo();// room.getPresence() equals { selectedId: "xxx" }room.history.redo();// room.getPresence() equals { selectedId: "yyy" }

Room.history.canUndo

Returns whether there are any operations to undo.

room.updatePresence({ selectedId: "xx" }, { addToHistory: true });// room.history.canUndo() is trueroom.history.undo();// room.history.canUndo() is false

Room.history.canRedo

Returns whether there are any operations to redo.

room.updatePresence({ selectedId: "xx" }, { addToHistory: true });room.history.undo();// room.history.canRedo() is trueroom.history.redo();// room.history.canRedo() is false

Room.history.pause

All future modifications made on the Room will be merged together to create a single history item until resume is called.

room.updatePresence({ cursor: { x: 0, y: 0 } }, { addToHistory: true });room.history.pause();room.updatePresence({ cursor: { x: 1, y: 1 } }, { addToHistory: true });room.updatePresence({ cursor: { x: 2, y: 2 } }, { addToHistory: true });room.history.resume();room.history.undo();// room.getPresence() equals { cursor: { x: 0, y: 0 } }

Room.history.resume

Resumes history. Modifications made on the Room are not merged into a single history item anymore.

room.updatePresence({ cursor: { x: 0, y: 0 } }, { addToHistory: true });room.history.pause();room.updatePresence({ cursor: { x: 1, y: 1 } }, { addToHistory: true });room.updatePresence({ cursor: { x: 2, y: 2 } }, { addToHistory: true });room.history.resume();room.history.undo();// room.getPresence() equals { cursor: { x: 0, y: 0 } }

Room.getStorageStatus

Get the storage status.

  • not-loaded: Initial state when entering the room.
  • loading: Once the storage has been requested via room.getStorage().
  • synchronizing: When some local updates have not been acknowledged by Liveblocks servers.
  • synchronized: Storage is in sync with Liveblocks servers.
const status = room.getStorageStatus();

Room.reconnect

Close the room connection and try to reconnect.

room.reconnect();

Storage

The room’s storage is a conflicts-free state that multiple users can edit at the same time. It persists even after everyone leaves the room. Liveblocks provides 3 data structures that can be nested to create the state that you want.

  • LiveObject - Similar to JavaScript object. Use this for storing records with fixed key names and where the values don’t necessarily have the same types. For example, a Person with a name (string) and an age (number) field.

    If multiple clients update the same property simultaneously, the last modification received by the Liveblocks servers is the winner.

  • LiveList - An ordered collection of items synchronized across clients. Even if multiple users add/remove/move elements simultaneously, LiveList will solve the conflicts to ensure everyone sees the same collection of items.

  • LiveMap - Similar to a JavaScript Map. Use this for indexing values that all have the same structure. For example, to store an index of Person values by their name. If multiple users update the same property simultaneously, the last modification received by the Liveblocks servers is the winner.

Room.getStorage

Get the room’s storage asynchronously (returns a Promise). The promise will resolve once the storage’s root is loaded and available.

The storage’s root will always be a LiveObject.

const { root } = await room.getStorage();

LiveObject

The LiveObject class is similar to a JavaScript object that is synchronized on all clients. Use this for storing records with fixed key names and where the values don’t necessarily have the same types. For example, a Person with a name (string) and an age (number) field.

Keys should be strings, and values should be serializable to JSON.

If multiple clients update the same property simultaneously, the last modification received by the Liveblocks servers is the winner.

new LiveObject

Create an empty LiveObject

const object = new LiveObject();

Create a LiveObject with initial data.

const object = new LiveObject({ firstName: "Margaret", lastName: "Hamilton" });

delete

Delete a property from the LiveObject

const object = new LiveObject({ firstName: "Ada", lastName: "Lovelace" });object.delete("lastName");object.toObject(); // equals to { firstName: "Ada" }

get

Get a property from the LiveObject

const object = new LiveObject({ firstName: "Ada", lastName: "Lovelace" });object.get("firstName"); // equals to "Ada"

set

Adds or updates a property with the specified key and a value.

const object = new LiveObject({ firstName: "Marie" });object.set("lastName", "Curie");

update

Adds or updates multiple properties at once.

const object = new LiveObject({ firstName: "Grace", lastName: "Hopper" });object.update({ job: "Computer Scientist", birthDate: "December 9, 1906" });

toObject

Transform the LiveObject into a normal JavaScript object.

const liveObject = new LiveObject({ firstName: "Grace", lastName: "Hopper" });liveObject.toObject();// { firstName: "Grace", lastName: "Hopper" }

Please note that this method won’t recursively convert Live structures, which may be surprising:

const liveObject = new LiveObject({  animals: new LiveList(["🦁", "🦊", "🐵"]),});liveObject.toObject();// { animals: <LiveList instance> } // ❗️

toImmutable

Returns an immutable JavaScript object that is equivalent to the LiveObject. Nested values will also be immutable.

const liveObject = new LiveObject({  firstName: "Grace",  lastName: "Hopper",  hobbies: new LiveList(["needlepoint", "reading", "playing piano"]),});liveObject.toImmutable();// {//   firstName: "Grace",//   lastName: "Hopper",//   hobbies: ["needlepoint", "reading", "playing piano"]// }

LiveMap

The LiveMap class is similar to a JavaScript Map that is synchronized on all clients.

Use this for indexing values that all have the same structure. For example, to store an index of Person values by their name. Keys should be strings, and values should be serializable to JSON. If multiple clients update the same property simultaneously, the last modification received by the Liveblocks servers is the winner.

new LiveMap

Create an empty LiveMap.

const map = new LiveMap();

Create a LiveMap with initial data.

const map = new LiveMap([  ["keyA", "valueA"],  ["keyB", "valueB"],]);

delete

Removes the specified element by key. Returns true if an element existed and has been removed, or false if the element does not exist.

const map = new LiveMap([  ["keyA", "valueA"],  ["keyB", "valueB"],]);map.delete("keyA"); // equals truemap.get("keyA"); // equals undefinedmap.delete("unknownKey"); // equals false

entries

Returns a new Iterator object that contains the [key, value] pairs for each element.

for (const [key, value] of map.entries()) {  // Iterate over all the keys and values of the map}

forEach

Executes a provided function once per each key/value pair in the Map object.

const map = new LiveMap([  ["keyA", "valueA"],  ["keyB", "valueB"],]);map.forEach((value, key) => console.log(value));// prints to the console "valueA", "valueB"

get

Returns a specified element from the LiveMap or undefined if the key can’t be found.

const map = new LiveMap([  ["keyA", "valueA"],  ["keyB", "valueB"],]);map.get("keyA"); // equals "valueA"map.get("unknownKey"); // equals undefined

has

Returns a boolean indicating whether an element with the specified key exists or not.

const map = new LiveMap([  ["keyA", "valueA"],  ["keyB", "valueB"],]);map.has("keyA"); // equals truemap.has("unknownKey"); // equals false

keys

Returns a new Iterator object that contains the keys for each element.

for (const key of map.keys()) {  // Iterate over all the keys and values of the map}

set

Adds or updates an element with a specified key and a value.

const map = new LiveMap();map.set("keyA", "valueA");

size

Returns the number of elements in the LiveMap.

const map = new LiveMap([  ["keyA", "valueA"],  ["keyB", "valueB"],]);map.size; // equals 2

values

Returns a new Iterator object that contains the the values for each element.

for (const value of map.values()) {  // Iterate over all the values of the map}

toImmutable

Returns an immutable ES6 Map that is equivalent to the LiveMap. Nested values will also be immutable.

const map = new LiveMap([  ["abc", new LiveObject({ firstName: "Grace", lastName: "Hopper" })],  ["pqr", new LiveObject({ firstName: "Ada", lastName: "Lovelace" })],]);map.toImmutable();// equal to:// Map(2) {//   'abc' => { firstName: 'Grace', lastName: 'Hopper' },//   'pqr' => { firstName: 'Ada', lastName: 'Lovelace' }// }

LiveList

The LiveList class represents an ordered collection of items that is synchorinized across clients. Items should be serializable to JSON or another Live data type.

new LiveList

Create an empty LiveList.

const list = new LiveList();

Create a LiveList with initial data.

const list = new LiveList(["🦁", "🦊", "🐵"]);

clear

Removes all the elements.

const list = new LiveList(["🦁", "🦊", "🐵"]);list.clear();list.toArray(); // equals []

delete

Deletes an element at the specified index.

const list = new LiveList(["🦁", "🦊", "🐵"]);list.delete(1);list.toArray(); // equals ["🦁", "🐵"]

every

Tests whether all elements pass the test implemented by the provided function. Returns true if the predicate function returns a truthy value for every element. Otherwise, false.

const list = new LiveList([0, 2, 4]);list.every((i) => i % 2 === 0); // equals truelist.push(5);list.every((i) => i % 2 === 0); // equals false

filter

Creates an array with all elements that pass the test implemented by the provided function.

const list = new LiveList([0, 1, 2, 3, 4]);list.filter((i) => i % 2 === 0); // equals [0, 2, 4]

find

Returns the first element that satisfies the provided testing function.

const list = new LiveList(["apple", "lemon", "tomato"]);list.find((fruit) => fruit.startsWith("l")); // equals "lemon"

findIndex

Returns the index of the first element in the LiveList that satisfies the provided testing function.

const list = new LiveList(["apple", "lemon", "tomato"]);list.findIndex((fruit) => fruit.startsWith("l")); // equals 1

forEach

Executes a provided function once for each element.

const list = new LiveList(["🦁", "🦊", "🐵"]);list.forEach((item) => console.log(item)); // prints to the console "🦁", "🦊", "🐵"

get

Get the element at the specified index.

const list = new LiveList(["🦁", "🦊", "🐵"]);list.get(2); // equals "🐵"

indexOf

Returns the first index at which a given element can be found in the LiveList, or -1 if it is not present.

const list = new LiveList(["🦁", "🦊", "🐵"]);list.indexOf("🐵"); // equals 2list.indexOf("🐺"); // equals -1

insert

Inserts one element at a specified index.

const list = new LiveList(["🦁", "🦊", "🐵"]);list.insert("🐺", 1);list.toArray(); // equals ["🦁", "🐺", "🦊", "🐵"]

lastIndexOf

Returns the last index at which a given element can be found in the LiveList, or -1 if it is not present. The LiveList is searched backwards, starting at fromIndex.

const list = new LiveList(["🦁", "🦊", "🐵", "🦊"]);list.lastIndexOf("🦊"); // equals 3list.lastIndexOf("🐺"); // equals -1

length

Returns the number of elements.

const list = new LiveList(["🦁", "🦊", "🐵"]);list.length; // equals 3

map

Creates an array populated with the results of calling a provided function on every element.

const list = new LiveList(["apple", "lemon", "tomato"]);list.map((fruit) => fruit.toUpperCase()); // equals ["APPLE", "LEMON", "TOMATO"]

move

Moves one element at a specified index.

const list = new LiveList(["🦁", "🦊", "🐵"]);list.move(2, 0); // move the "🐵" at index 0list.toArray(); // equals ["🐵", "🦁", "🦊"]

push

Adds one element to the end of the LiveList.

const list = new LiveList(["🦁", "🦊", "🐵"]);list.push("🐺");list.toArray(); // equals ["🦁", "🦊", "🐵", "🐺"]

set

Replace one element at the specified index.

const list = new LiveList(["🦁", "🦊", "🐵"]);list.set(1, "🐺");list.toArray(); // equals ["🦁", "🐺", "🐵"]

some

Tests whether at least one element in the LiveList passes the test implemented by the provided function.

const list = new LiveList(["apple", "lemon", "tomato"]);list.some((fruit) => fruit.startsWith("l")); // equals truelist.some((fruit) => fruit.startsWith("x")); // equals false

toArray

Transforms the LiveList into a normal JavaScript array.

const list = new LiveList(["🦁", "🦊", "🐵"]);list.toArray();// ["🦁", "🦊", "🐵"]

Please note that this method won’t recursively convert Live structures, which may be surprising:

const list = new LiveList([  new LiveObject({ firstName: "Grace", lastName: "Hopper" }),]);list.toArray();// [ <LiveObject instance> ]  // ❗️

toImmutable

Returns an immutable JavaScript array that is equivalent to the LiveList. Nested values will also be immutable.

const list = new LiveList([  new LiveObject({ firstName: "Grace", lastName: "Hopper" }),]);list.toImmutable();// [ { firstName: "Grace", lastName: "Hopper" } ]