API Reference - @liveblocks/client

@liveblocks/client provides you with JavaScript bindings for our real-time collaboration APIs, built on top of WebSockets. Read our getting started guides to learn more.

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: "",});

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/liveblocks-auth" });

createClient with callback

If you need to add additional headers or use your own function to call the endpoint, authEndpoint can be provided as a custom callback.

Your async function should return either of the following values:

  1. A valid token. Return a { "token": "..." } shaped response.
  2. Explicitly forbid access. Return an { "error": "forbidden", "reason": "..." } shaped response. If you return this response, the client will disconnect and won't keep trying to authorize.

Any other error will be treated as an unexpected error, after which the client will retry making the request until it gets either (1) or (2).

Note that when using Notifications, the parameter room can be undefined. This implies that the client is requesting a token that grants access to multiple rooms, rather than a specific room.

You’re responsible for making the fetch to your own backend. Example implementation:

import { createClient } from "@liveblocks/client";
const client = createClient({ authEndpoint: async (room) => { const response = await fetch("/api/liveblocks-auth", { method: "POST", headers: { Authentication: "<your own headers here>", "Content-Type": "application/json", }, // Don't forget to pass `room` down, // and note that in some cases, `room` can be undefined. 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.

Background keep-alive timeout

By default, Liveblocks applications will maintain an active WebSocket connection to the Liveblocks servers, even when running in a browser tab that’s in the background. However, if you’d prefer for background tabs to disconnect after a period of inactivity, then you can use backgroundKeepAliveTimeout.

When backgroundKeepAliveTimeout is specified, the client will automatically disconnect applications that have been in an unfocused background tab for at least the specified time. When the browser tab is refocused, the client will immediately reconnect to the room and synchronize the document.

import { createClient } from "@liveblocks/client";
const client = createClient({ /* ... other options ... */ backgroundKeepAliveTimeout: 15 * 60 * 1000 /* 15 minutes */,});

backgroundKeepAliveTimeout accepts a numeric value in milliseconds—we advise using a value of at least a few minutes, to avoid unnecessary disconnections.

resolveUsers

When using Comments, anything that is linked to a user (comments, mentions, etc.) will reference their ID. To display user information based on IDs, you can provide a resolver function to the resolveUsers option in createClient.

This resolver function will receive a list of user IDs and should return a list of user objects of the same size and in the same order.

import { createClient } from "@liveblocks/client";
const client = createClient({ /* ... other options ... */ resolveUsers: async ({ userIds }) => { const usersData = await (userIds);
return usersData.map((userData) => ({ name: userData.name, avatar: userData.avatar.src, })); },});

resolveRoomsInfo

When using Notifications with Comments, room IDs will be used to contextualize notifications (e.g. "Chris mentioned you in room-id"). To replace room IDs with more fitting names (e.g. document names, "Chris mentioned you in Document A"), you can provide a resolver function to the resolveRoomsInfo option in createClient.

This resolver function will receive a list of room IDs and should return a list of room info objects of the same size and in the same order.

import { createClient } from "@liveblocks/client";
const client = createClient({ /* ... other options ... */ resolveRoomsInfo: async ({ roomIds }) => { const documentsData = await (roomIds);
return documentsData.map((documentData) => ({ name: documentData.name, // url: documentData.url, })); },});

In addition to the room’s name, you can also provide a room’s URL as the url property. If you do so, the InboxNotification component will automatically use it. It’s possible to use an inbox notification’s roomId property to construct a room’s URL directly in React and set it on InboxNotification via href, but the room ID might not be enough for you to construct the URL , you might need to call your backend for example. In that case, providing it via resolveRoomsInfo is the preferred way.

resolveMentionSuggestions

To enable creating mentions in Comments, you can provide a resolver function to the resolveMentionSuggestions option in createClient.

This resolver function will receive the mention currently being written (e.g. when writing “@jane”, text will be jane) and should return a list of user IDs matching that text. This function will be called every time the text changes but with some debouncing.

import { createClient } from "@liveblocks/client";
const client = createClient({ /* ... other options ... */ resolveMentionSuggestions: async ({ text, roomId }) => { const workspaceUsers = await (roomId);
if (!text) { // Show all workspace users by default
return (workspaceUsers); } else { const matchingUsers = (workspaceUsers, text);
return (matchingUsers); } },});

Client

Client returned by createClient.

Client.enterRoom

Enters a room and returns both the local Room instance, and a “leave” function (unsubscribe function). 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.
  • autoConnect (optional) - Whether or not the room immediately 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, leave } = client.enterRoom("my-room", {  initialPresence: { cursor: null },  initialStorage: { todos: new LiveList() },});
// Don’t forget to call `leave()` when you’re done with the roomleave();

Client.enter

Enters a room and returns its local Room instance.

// ❌ This API was recommended before 1.5const room = client.enter("my-room", {  initialPresence: { cursor: null },  initialStorage: { todos: new LiveList() },});client.leave(roomId);
// ✅ Prefer this insteadconst { room, leave } = client.enterRoom("my-room", { initialPresence: { cursor: null }, initialStorage: { todos: new LiveList() },});leave();

Client.leave

Leaves a room.

// ❌ This API was recommended before 1.5client.leave("my-room");
// ✅ Prefer this insteadconst { room, leave } = client.enterRoom("my-room" /* options */);leave();

Client.getRoom

Gets a room by its ID. Returns null if client.enterRoom has not been called previously.

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

You’ll likely not need this API if you’re using the newer client.enterRoom API.

Client.logout

Purges any auth tokens from the client’s memory. If there are any rooms that are still connected, they will be forced to reauthorize.

client.logout();

Call this API if you have a single page application (SPA) and you want to log your user out.

Room

Room returned by client.enterRoom (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. You need only pass the properties you wish to update—any changes will be merged into the current presence. The entire presence object will not be replaced.

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 is a “fire and forget” action. If the sending client is not currently connected to a room, the event is simply discarded. When passing the shouldQueueEventIfNotReady option, the client will queue up the event, and only send it once the connection to the room is (re)established.

// On client Aroom.broadcastEvent({ type: "EMOJI", emoji: "🔥" });
// On client Broom.subscribe("event", ({ event, user, connectionId }) => { // ^^^^ Will be Client A if (event.type === "EMOJI") { // Do something }});

The user property will indicate which User instance sent the message. This will typically be equal to one of the others in the room, but it can also be null in case this event was broadcasted from the server, using the Broadcast Event API.

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 broadcast by Room.broadcastEvent.

Takes a callback that is called when a user calls Room.broadcastEvent. Provides the event along with the user and their connectionId of the user that sent the message. If this event was sent via the Broadcast to a room REST API, user will be null and connectionId will be -1.

Returns an unsubscribe function.

// On client Aroom.broadcastEvent({ type: "EMOJI", emoji: "🔥" });
// On client Bconst unsubscribe = room.subscribe("event", ({ event, user, connectionId }) => { // ^^^^ Will be Client A if (event.type === "EMOJI") { // 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 unrecoverable room connection errors. This event will be emitted right before the client disconnects and won’t try reconnecting again.

Returns an unsubscribe function.

const unsubscribe = room.subscribe("error", (error) => {  switch (error.code) {    case -1:      // Authentication error      break;
case 4001: // Could not connect because you don't have access to this room break;
case 4005: // Could not connect because room was full break;
case 4006: // The room ID has changed, get the new room ID (use this for redirecting) const newRoomId = error.message; break;
default: // Unexpected error break; }});

You can use this event to trigger a “Not allowed” screen/dialog. If you’d like to retry connecting, call room.reconnect().

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 Storage and Presence modifications made during the given function. Each modification is grouped together, which means that other clients receive the changes as a single message after the batch function has run. Every modification made during the batch is merged into a single history item (undo/redo).

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

Note that room.batch cannot take an async function.

// ❌ Won't workroom.batch(async () => /* ... */);
// ✅ Will workroom.batch(() => /*... */);

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

Clears the undo and redo stacks for the current client. Explicitly clearing history resets the ability to undo beyond the current document state. Other clients’ histories are unaffected.

room.updatePresence({ selectedId: "xx" }, { addToHistory: true });// room.history.canUndo() is trueroom.history.clear();// room.history.canUndo() 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.connect

Connect the local room instance to the Liveblocks server. Does nothing if the room is already connecting, reconnecting or connected.

room.connect();

We don’t recommend using this API directly.

Room.reconnect

Reconnect the local room instance to the Liveblocks server, using a new WebSocket connection.

room.reconnect();

Room.disconnect

Disconnect the local room instance from the Liveblocks server. The room instance will remain functional (for example, it will still allow local presence or storage mutations), but since it’s no longer connected, changes will not be persisted or synchronized until the room instance is reconnected again.

room.disconnect();

We don’t recommend using this API directly.

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"]// }

clone

Returns a deep copy of the LiveObject that can be inserted elsewhere in the Storage tree.

const obj = new LiveObject(/* ... */);root.set("a", obj);root.set("b", obj.clone());

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' }// }

clone

Returns a deep copy of the LiveMap that can be inserted elsewhere in the Storage tree.

const map = new LiveMap(/* ... */);root.set("a", map);root.set("b", map.clone());

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" } ]

clone

Returns a deep copy of the LiveList that can be inserted elsewhere in the Storage tree.

const list = new LiveList(/* ... */);root.set("a", list);root.set("b", list.clone());

Utilities

getMentionedIdsFromCommentBody

Returns an array of each user’s ID that has been mentioned in a CommentBody (found under comment.body).

import { getMentionedIdsFromCommentBody } from "@liveblocks/client";
const mentionedIds = getMentionedIdsFromCommentBody(comment.body);

Here’s an example with a custom CommentBody.

import {  CommentBody,  getMentionedIdsFromCommentBody,} from "@liveblocks/client";
// Create a custom `CommentBody`const commentBody: CommentBody = { version: 1, content: [ { type: "paragraph", children: [ { text: "Hello " }, { type: "mention", id: "chris@example.com" }, ], }, ],};
// Get the mentions inside the comment's bodyconst mentionedIds = getMentionedIdsFromCommentBody(commentBody);
// ["chris@example.com"]console.log(mentionedIds);

stringifyCommentBody

Used to convert a CommentBody (found under comment.body) into either a plain string, Markdown, HTML, or a custom format.

import { stringifyCommentBody } from "@liveblocks/client";
const stringComment = await stringifyCommentBody(comment.body);
// "Hello marc@example.com from https://liveblocks.io"console.log(stringComment);

A number of options are available.

import { stringifyCommentBody } from "@liveblocks/client";
const stringComment = await stringifyCommentBody(comment.body, { // Optional, convert to specific format, "plain" (default) | "markdown" | "html" format: "markdown",
// Optional, supply a separator to be used between paragraphs separator: `\n\n`,
// Optional, override any elements in the CommentBody with a custom string elements: { // Optional, override the `paragraph` element paragraph: ({ element, children }) => `<p>${children}</p>`,
// Optional, override the `text` element text: ({ element }) => element.bold ? `<strong>${element.text}</strong>` : `${element.text}`,
// Optional, override the `link` element link: ({ element, href }) => `<a href="${href}" target="_blank">${element.url}</a>`,
// Optional, override the `mention` element. `user` available if `resolveUsers` supplied mention: ({ element, user }) => `<a href="${user.profileUrl}">${element.id}</a>`, },
// Optional, get your user's names and info from their ID to be displayed in mentions async resolveUsers({ userIds }) { const usersData = await (userIds);
return usersData.map((userData) => ({ // Name is inserted into the output instead of a user's ID name: userData.name,
// Custom formatting in `elements.mention` allows custom properties to be used profileUrl: userData.profileUrl, })); },});

Formatting examples

Here are a number of different formatting examples derived from the same CommentBody.

// "Hello marc@example.com from https://liveblocks.io"await stringifyCommentBody(comment.body);
// "Hello @Marc from https://liveblocks.io"await stringifyCommentBody(comment.body, { resolveUsers({ userIds }) { return [{ name: "Marc" }]; },});
// "**Hello** @Marc from [https://liveblocks.io](https://liveblocks.io)"await stringifyCommentBody(comment.body, { format: "markdown",
resolveUsers() { return [{ name: "Marc" }]; },});
// "<b>Hello</b> <span data-mention>@Marc</span> from// <a href="https://liveblocks.io">https://liveblocks.io</a>"await stringifyCommentBody(comment.body, { format: "html",
resolveUsers() { return [{ name: "Marc" }]; },});
// "<b>Hello</b> <a href="https://example.com" data-id="marc@example.com">@Marc</a> from// <a href="https://liveblocks.io">https://liveblocks.io</a>"await stringifyCommentBody(comment.body, { format: "html",
mention: ({ element, user }) => `<a href="${user.profileUrl}" data-id="${element.id}">${user.name}</a>`,
resolveUsers() { return [{ name: "Marc", profileUrl: "https://example.com" }]; },});