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 setup 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 setup 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 throttle the WebSocket messages sent to 100 milliseconds.

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

throttle should be between 80 and 1000 milliseconds.

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

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. Event broadcasted to the room can be listened 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. With the option shouldQueueEventIfNotReady , the event will be queued and sent once the connection is established.

Notice

We are not sure if we want to support this option in the future so it might be deprecated to be replaced by something else.

// 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.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 } object.

const { root } = await room.getStorage();const unsubscribe = room.subscribe(  root,  (updates) => {    for (const update of updates) {      const {        type, // "update" or "delete"        node,      } = update;      switch (node.type) {        case "LiveObject": {          // update.node is the LiveObject that has been updated/deleted          break;        }        case "LiveMap": {          // update.node is the LiveMap that has been updated/deleted          break;        }        case "LiveList": {          // update.node is the LiveList that has been updated/deleted          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) => {  // Do something});

Room.subscribe("connection")

Subscribe to the WebSocket connection status updates.

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

The value can be : authenticating, connecting, open, failed, closed or unavailable.

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.

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

Room.subscribe("storage-status")

Subscribe to storage status changes.

Returns an unsubscribe function.

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 storage block is in beta

The following APIs are subject to change during the beta.

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 storageโ€™s root is 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

Will be deprecated in the future

Starting with 0.18, we recommend toImmutable instead. Itโ€™s faster, cached, and leads to fewer surprises.

Transform the LiveObject into a normal JavaScript object.

const liveObject = new LiveObject({ firstName: "Grace", lastName: "Hopper" });// { 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}
Iteration with TypeScript

If your TypeScript project targets es5 or lower, youโ€™ll need to enable the --downlevelIteration option to use this API.

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}
Iteration with TypeScript

If your TypeScript project targets es5 or lower, youโ€™ll need to enable the --downlevelIteration option to use this API.

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}
Iteration with TypeScript

If your TypeScript project targets es5 or lower, youโ€™ll need to enable the --downlevelIteration option to use this API.

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 structure.

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

Will be deprecated in the future

Starting with 0.18, we recommend toImmutable instead. Itโ€™s faster, cached, and leads to fewer surprises.

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" } ]
ยฉ 2023 Liveblocks Inc.Edit this page