We use cookies to collect data to improve your experience on our site. Read our [Privacy Policy](/content/privacy/index.html) to learn more.

DeclineAccept

# API Reference \- @liveblocks/client

`@liveblocks/client` provides you with JavaScript bindings for our realtime
collaboration APIs, built on top of WebSockets. Read our
[getting started](/content/docs/get-started/index.html) guides to learn more.

## [createClient](/content/docs/api-reference/liveblocks-client\#createClient/index.html)

Creates a [client](/content/docs/api-reference/liveblocks-client#Client/index.html) that allows you to connect to Liveblocks servers.
You must define either `authEndpoint` or `publicApiKey`. Resolver functions
should be placed inside here, and a number of other options are available.

```tsx
import { createClient } from "@liveblocks/client";
const client = createClient({  authEndpoint: "/api/liveblocks-auth",
  // Other options  // ...});
```

Every createClient option

Returns

- clientClient

Returns a [Client](/content/docs/api-reference/liveblocks-client#Client/index.html), used for connecting to Liveblocks.

Options

- authEndpointSee full type

The URL of your back end’s [authentication endpoint](/content/docs/authentication/index.html)
as a string, or an async callback function that returns a Liveblocks token
result. Either `authEndpoint` or `publicApiKey` are required. Learn more
about [using a URL string](/content/docs/api-reference/liveblocks-client#createClientAuthEndpoint/index.html) and [using a\\
callback](/content/docs/api-reference/liveblocks-client#createClientCallback/index.html).

- publicApiKeystring

The public API key taken from your project’s
[dashboard](/content/dashboard/apikeys/index.html). Generally not recommended for production
use. Either `authEndpoint` or `publicApiKey` are required. [Learn\\
more](/content/docs/api-reference/liveblocks-client#createClientPublicKey/index.html).

- throttlenumberDefault is100

The throttle time between WebSocket messages in milliseconds, a number
between `16` and `1000` is allowed. Using `16` means your app will update 60
times per second. [Learn more](/content/docs/api-reference/liveblocks-client#createClientThrottle/index.html).

- preventUnsavedChangesbooleanDefault isfalse

When set, navigating away from the current page is prevented while
Liveblocks is still synchronizing local changes. [Learn\\
more](/content/docs/api-reference/liveblocks-client#prevent-users-losing-unsaved-changes/index.html).

- lostConnectionTimeoutnumberDefault is5000

After a user disconnects, the time in milliseconds before a
[`"lost-connection"`](/content/docs/api-reference/liveblocks-client#Room.subscribe.lost-connection/index.html)
event is fired. [Learn more](/content/docs/api-reference/liveblocks-client#createClientLostConnectionTimeout/index.html).

- backgroundKeepAliveTimeoutnumber

The time before an inactive WebSocket connection is disconnected. This is
disabled by default, but setting a number will activate it. [Learn\\
more](/content/docs/api-reference/liveblocks-client#createClientBackgroundKeepAliveTimeout/index.html).

- resolveUsersSee full type

A function that resolves user information in
[Comments](/content/docs/ready-made-features/comments/index.html), [Text\\
Editor](/content/docs/ready-made-features/text-editor/index.html), and
[Notifications](/content/docs/ready-made-features/notifications/index.html). Return an array of
`UserMeta["info"]` objects in the same order they arrived. [Learn\\
more](/content/docs/api-reference/liveblocks-client#createClientResolveUsers/index.html).

- resolveRoomsInfoSee full type

A function that resolves room information in
[Notifications](/content/docs/ready-made-features/notifications/index.html). Return an array of
`RoomInfo` objects in the same order they arrived. [Learn\\
more](/content/docs/api-reference/liveblocks-client#createClientResolveRoomsInfo/index.html).

- resolveGroupsInfoSee full type

A function that resolves group information in
[Comments](/content/docs/ready-made-features/comments/index.html) and [Text\\
Editor](/content/docs/ready-made-features/text-editor/index.html). Return an array of
`GroupInfo` objects in the same order they arrived. [Learn\\
more](/content/docs/api-reference/liveblocks-client#createClientResolveGroupsInfo/index.html).

- resolveMentionSuggestionsSee full type

A function that resolves mention suggestions in
[Comments](/content/docs/ready-made-features/comments/index.html) and [Text\\
Editor](/content/docs/ready-made-features/text-editor/index.html). Return an array of user IDs
or mention objects. [Learn more](/content/docs/api-reference/liveblocks-client#createClientResolveMentionSuggestions/index.html).

- polyfills

Place polyfills for `atob`, `fetch`, and `WebSocket` inside here. Useful
when using a non-browser environment, such as [Node.js](/content/docs/api-reference/liveblocks-client#createClientNode/index.html)
or [React Native](/content/docs/api-reference/liveblocks-client#createClientReactNative/index.html).

- badgeLocationSee full typeDefault is"bottom-right"

The location of the "Powered by Liveblocks" badge. Can be set to either
`"top-right"`, `"bottom-right"`, `"bottom-left"`, or `"top-left"`. [Learn\\
more](/content/docs/api-reference/liveblocks-client#createClientBadgeLocation/index.html).

- unstable\_streamDatabooleanDefault isfalseDeprecated

Deprecated. For new rooms, use [`engine: 2`](/content/docs/api-reference/liveblocks-client#Client.enterRoom) instead.
Engine 2 rooms have native support for streaming. This flag will be removed
in a future version, but will continue to work for existing engine 1 rooms
for now. [Learn more](/content/docs/guides/the-new-storage-engine-and-its-benefits/index.html).

### [createClient with public key](/content/docs/api-reference/liveblocks-client\#createClientPublicKey/index.html)

When creating a client with a public key, you don’t need to set up an
authorization endpoint. We only recommend using a public key when prototyping,
or on public landing pages, as it makes it possible for end users to access any
room’s data. You should instead use an
[auth endpoint](/content/docs/api-reference/liveblocks-client#createClientAuthEndpoint/index.html).

[Sign in](/content/dashboard/apikeys/index.html) to get your own API keys.

```ts
import { createClient } from "@liveblocks/client";
const client = createClient({  publicApiKey: "pk_prod_xxxxxxxxxx…xxxxxxxxpk_prod_xxxxxxxxxxxxxxxxxxxxxxxx",});
```

### [createClient with auth endpoint](/content/docs/api-reference/liveblocks-client\#createClientAuthEndpoint/index.html)

If you are not using a public key, you need to set up your own `authEndpoint`.
Please refer to our [Authentication guide](/content/docs/authentication/index.html).

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

### [createClient with auth endpoint callback](/content/docs/api-reference/liveblocks-client\#createClientCallback/index.html)

If you need to add additional headers or use your own function to call your
endpoint, `authEndpoint` can be provided as a custom callback. You should return
the token created with
[`Liveblocks.prepareSession`](/content/docs/api-reference/liveblocks-node#access-tokens/index.html)
or [`liveblocks.identifyUser`](/content/docs/api-reference/liveblocks-node#id-tokens/index.html),
learn more in [authentication guide](/content/docs/rooms/authentication/index.html).

```ts
import { createClient } from "@liveblocks/client";
const client = createClient({  authEndpoint: async (room) => {    // Fetch your authentication endpoint and retrieve your access or ID token    // ...
    return { token: "..." };  },});
```

`room` is the room ID that the user is connecting to. When using
[Notifications](/content/docs/ready-made-features/comments/email-notifications/index.html), `room`
can be `undefined`, as the client is requesting a token that grants access to
multiple rooms, rather than a specific room.

#### [Fetch your endpoint](/content/docs/api-reference/liveblocks-client\#Fetch-your-endpoint/index.html)

Here’s an example of fetching your API endpoint at `/api/liveblocks-auth` within
the callback.

```ts
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. Note that it      // can be undefined when using Notifications.      body: JSON.stringify({ room }),    });    return await response.json();  },});
```

#### [Token details](/content/docs/api-reference/liveblocks-client\#Token-details/index.html)

You should return the token created with
[`Liveblocks.prepareSession`](/content/docs/api-reference/liveblocks-node#access-tokens/index.html)
or [`liveblocks.identifyUser`](/content/docs/api-reference/liveblocks-node#id-tokens/index.html).
These are the values the functions can return.

1. A valid token, it returns a `{ "token": "..." }` shaped response.
2. A token that explicitly forbids access, it returns an
`{ "error": "forbidden", "reason": "..." }` shaped response. If this is
returned, 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 the request until it receives either 1. or 2.

### [WebSocket throttle](/content/docs/api-reference/liveblocks-client\#createClientThrottle/index.html)

By default, the client throttles the WebSocket messages sent to one every 100
milliseconds, which translates to 10 updates per second. It’s possible to
override that configuration with the `throttle` option with a value between `16`
and `1000` milliseconds.

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

This option is helpful for smoothing out realtime animations in your
application, as you can effectively increase the framerate without using any
interpolation. Here are some examples with their approximate frames per second
(FPS) values.

```ts
throttle:  16, // 60 FPSthrottle:  32, // 30 FPSthrottle: 200, //  5 FPS
```

### [Prevent users losing unsaved changes](/content/docs/api-reference/liveblocks-client\#prevent-users-losing-unsaved-changes/index.html)

Liveblocks usually synchronizes milliseconds after a local change, but if a user
immediately closes their tab, or if they have a slow connection, it may take
longer for changes to synchronize. Enabling `preventUnsavedChanges` will stop
tabs with unsaved changes closing, by opening a dialog that warns users. In
usual circumstances, it will very rarely trigger.

```tsx
import { createClient } from "@liveblocks/client";
const client = createClient({  preventUnsavedChanges: true,
  // Other options  // ...});
```

More specifically, this option triggers when:

- There are unsaved changes after calling any hooks or methods, in all of our
products.
- There are unsaved changes in a
[Text Editor](/content/docs/ready-made-features/text-editor/index.html).
- There’s an unsubmitted comment in the
[Composer](/content/docs/api-reference/liveblocks-react-ui#Composer/index.html).
- The user has made changes and is currently offline.

Internally, this option uses the
[beforeunload event](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event).

### [Lost connection timeout](/content/docs/api-reference/liveblocks-client\#createClientLostConnectionTimeout/index.html)

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

How quickly this event is triggered can be configured with the
`lostConnectionTimeout` setting, and it takes a number in milliseconds.
`lostConnectionTimeout` can be set between `1000` and `30000` milliseconds. The
default is `5000`, or 5 seconds.

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

You can listen to the event with [`room.subscribe("lost-connection")`](/content/docs/api-reference/liveblocks-client#Room.subscribe.lost-connection/index.html). Note
that this also affects when `others` are reset to an empty array after a
disconnection. This helps prevent temporary flashes in your application as a
user quickly disconnects and reconnects. For a demonstration of this behavior,
see our [connection status example](/content/examples/connection-status/nextjs/index.html).

### [Background keep-alive timeout](/content/docs/api-reference/liveblocks-client\#createClientBackgroundKeepAliveTimeout/index.html)

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.

```ts
import { createClient } from "@liveblocks/client";
const client = createClient({  // Disconnect users after 15 minutes of inactivity  backgroundKeepAliveTimeout: 15 * 60 * 1000,
  // Other options  // ...});
```

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

### [resolveUsers](/content/docs/api-reference/liveblocks-client\#createClientResolveUsers/index.html)

[Comments](/content/docs/ready-made-features/comments/index.html) and
[Text Editor](/content/docs/ready-made-features/text-editor/index.html) store user IDs in their
system, but no other user information. To display user information in Comments,
Text Editor, and Notifications components, such as a user’s name or avatar, you
need to resolve these IDs into user objects. This function receives a list of
user IDs and you should return a list of user objects of the same size, in the
same order.

User IDs are automatically resolved in batches with a maximum of 50 users per
batch to optimize performance and prevent overwhelming your user resolution
function.

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

The name and avatar you return are rendered in
[`Thread`](/content/docs/api-reference/liveblocks-react-ui#Thread/index.html) components.

#### [User objects](/content/docs/api-reference/liveblocks-client\#User-objects/index.html)

The user objects returned by the resolver function take the shape of
`UserMeta["info"]`, which contains `name` and `avatar` by default. These two
values are optional, though if you’re using the
[Comments default components](/content/docs/api-reference/liveblocks-react-ui#Components/index.html),
they are necessary. Here’s an example of `userIds` and the exact values
returned.

```ts
resolveUsers: async ({ userIds }) => {  // ["marc@example.com", "nimesh@example.com"];  console.log(userIds);
  return [    { name: "Marc", avatar: "https://example.com/marc.png" },    { name: "Nimesh", avatar: "https://example.com/nimesh.png" },  ];};
```

You can also return custom information, for example, a user’s `color`:

```ts
resolveUsers: async ({ userIds }) => {  // ["marc@example.com"];  console.log(userIds);
  return [    {      name: "Marc",      avatar: "https://example.com/marc.png",      color: "purple",    },  ];};
```

#### [Accessing user data in React](/content/docs/api-reference/liveblocks-client\#Accessing-user-data-in-React/index.html)

You can access any values set within `resolveUsers` with the
[`useUser`](/content/docs/api-reference/liveblocks-react#useUser/index.html) hook.

```tsx
import { useUser } from "@liveblocks/react/suspense";
function Component() {  const user = useUser("marc@example.com");
  // { name: "Marc", avatar: "https://...", ... }  console.log(user);}
```

### [resolveRoomsInfo](/content/docs/api-reference/liveblocks-client\#createClientResolveRoomsInfo/index.html)

When using
[Notifications](/content/docs/ready-made-features/comments/email-notifications/index.html) with
[Comments](/content/docs/ready-made-features/comments/index.html), room IDs will be used to
contextualize notifications (e.g. “Chris mentioned you in _room-id_”) in the
[`InboxNotification`](/content/docs/api-reference/liveblocks-react-ui#InboxNotification/index.html)
component. 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`](/content/docs/api-reference/liveblocks-client#createClient/index.html).

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.

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

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`](/content/docs/api-reference/liveblocks-react-ui#InboxNotification/index.html)
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`](/content/docs/api-reference/liveblocks-react-ui#InboxNotification/index.html)
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.

### [resolveGroupsInfo](/content/docs/api-reference/liveblocks-client\#createClientResolveGroupsInfo/index.html)

When using group mentions with [Comments](/content/docs/ready-made-features/comments/index.html)
and [Text Editor](/content/docs/ready-made-features/text-editor/index.html), group IDs will be used
instead of user IDs. Similarly to [`resolveUsers`](/content/docs/api-reference/liveblocks-client#createClientResolveUsers/index.html),
you can provide a resolver function to the `resolveGroupsInfo` option in
[`createClient`](/content/docs/api-reference/liveblocks-client#createClient/index.html) to assign information like names and avatars to
group IDs.

```tsx
import { createClient } from "@liveblocks/client";
const client = createClient({  resolveGroupsInfo: async ({ groupIds }) => {    const groupsData = await __getGroupsFromDB__(groupIds);
    return groupsData.map((groupData) => ({      name: groupData.name,      avatar: groupData.avatar.src,    }));  },
  // Other options  // ...});
```

#### [Accessing group info in React](/content/docs/api-reference/liveblocks-client\#Accessing-group-info-in-React/index.html)

You can access any values set within `resolveGroupsInfo` with the
[`useGroupInfo`](/content/docs/api-reference/liveblocks-react#useGroupInfo/index.html) hook.

```tsx
import { useGroupInfo } from "@liveblocks/react/suspense";
function Component() {  const group = useGroupInfo("group-engineering");
  // { name: "Engineering", avatar: "https://...", ... }  console.log(group);}
```

### [resolveMentionSuggestions](/content/docs/api-reference/liveblocks-client\#createClientResolveMentionSuggestions/index.html)

To enable creating mentions in [Comments](/content/docs/ready-made-features/comments/index.html)
and [Text Editor](/content/docs/ready-made-features/text-editor/index.html), you can provide a
resolver function to the `resolveMentionSuggestions` option in
[`createClient`](/content/docs/api-reference/liveblocks-client#createClient/index.html). These mentions will be displayed in the
[`Composer`](/content/docs/api-reference/liveblocks-react-ui#Composer/index.html) component and in
text editors.

This resolver function will receive the mention currently being typed (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.

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

#### [Group mentions](/content/docs/api-reference/liveblocks-client\#Group-mentions/index.html)

To support group mentions in [Comments](/content/docs/ready-made-features/comments/index.html) and
[Text Editor](/content/docs/ready-made-features/text-editor/index.html), you can return a list of
mention objects instead of user IDs to suggest a mix of user and group mentions.

```tsx
import { createClient } from "@liveblocks/client";
const client = createClient({  resolveMentionSuggestions: async ({ text, roomId }) => {    const dbUsers = await __findUsersFromDB__(roomId);    const dbGroups = await __findGroupsFromDB__(roomId);
    // Show groups and users matching the text being typed    return [      ...dbGroups.map((group) => ({        kind: "group",        id: group.id,      })),      ...dbUsers.map((user) => ({        kind: "user",        id: user.id,      })),    ];  },
  // Other options  // ...});
```

The mention objects specify which kind of mention it is, the ID to mention (user
ID or group ID), etc.

```tsx
// A user mention suggestion{  kind: "user",  id: "user-1",}
// A group mention suggestion{  kind: "group",  id: "group-1",}
// A group mention suggestion with fixed group members// When using fixed group members via `userIds`, they will take precedence// if the group ID exists on Liveblocks.{  kind: "group",  id: "here",  members: ["user-1", "user-2"],}
```

### [createClient for Node.js](/content/docs/api-reference/liveblocks-client\#createClientNode/index.html)

To use `@liveblocks/client` in Node.js, you need to provide [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) and
[`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) polyfills. As polyfills, we recommend installing [`ws`](https://www.npmjs.com/package/ws) and
[`node-fetch`](https://npmjs.com/package/node-fetch).

Terminal

npm

```text
npm install ws node-fetch
```

Then, pass them to the `createClient` polyfill option as below.

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

Note that `node-fetch` v3+
[does not support CommonJS](https://github.com/node-fetch/node-fetch/blob/main/docs/v3-UPGRADE-GUIDE.md#converted-to-es-module).
If you are using CommonJS, downgrade `node-fetch` to v2.

### [createClient for React Native](/content/docs/api-reference/liveblocks-client\#createClientReactNative/index.html)

To use `@liveblocks/client` with [React Native](https://reactnative.dev/), you
need to add an [`atob`](https://developer.mozilla.org/en-US/docs/Web/API/atob) polyfill. As a polyfill, we recommend installing
[`base-64`](https://www.npmjs.com/package/base-64).

Terminal

npm

```text
npm install base-64
```

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

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

### [Powered by Liveblocks branding](/content/docs/api-reference/liveblocks-client\#createClientBadgeLocation/index.html)

By default, Liveblocks displays a "Powered by Liveblocks" badge in your
application. You can adjust the position of the badge by setting the
`badgeLocation` property on `createClient`.

Set badge location

```ts
import { createClient } from "@liveblocks/client";
// "top-right", "bottom-right", "bottom-left", "top-left"const client = createClient({  badgeLocation: "bottom-right",
  // ...});
```

If you wish to remove the badge entirely, you can do so by following these
steps:

1. In the Liveblocks dashboard, navigate to your
[team's settings](/content/dashboard/settings/index.html).
2. Under **General**, toggle on the remove "Powered by Liveblocks" branding
option.

Removing the "Powered by Liveblocks" badge

Removing the "Powered by Liveblocks" badge on your projects requires a
[paid plan](/content/pricing/index.html). See the [pricing page](/content/pricing/index.html) for more information.

## [Client](/content/docs/api-reference/liveblocks-client\#Client/index.html)

Client returned by [`createClient`](/content/docs/api-reference/liveblocks-client#createClient/index.html) which allows you to connect to Liveblocks
servers in your application, and enter rooms.

### [Client.enterRoom](/content/docs/api-reference/liveblocks-client\#Client.enterRoom)

Enters a room and returns both the local `Room` instance, and a `leave`
unsubscribe function. The authentication endpoint is called as soon as you call
this function. Used for setting [initial Presence](/content/docs/api-reference/liveblocks-client#setting-initial-presence/index.html)
and [initial Storage](/content/docs/api-reference/liveblocks-client#setting-initial-storage/index.html) values.

```ts
const { room, leave } = client.enterRoom("my-room-id", {  // Options  // ...});
```

Note that it’s possible to [add types to your room](/content/docs/api-reference/liveblocks-client#typing-your-data/index.html).

Returns

- roomRoom<Presence, Storage, UserMeta, RoomEvent>

A [Room](/content/docs/api-reference/liveblocks-client#Room/index.html), used for building your Liveblocks application. Learn more
about [typing your room](/content/docs/api-reference/liveblocks-client#typing-your-data/index.html).

- leave() =\> void

A function that’s used to leave the room and disconnect.

Arguments

- roomIdstringRequired

The ID of the room you’re connecting to.

- options.initialPresenceJsonObject

The initial Presence of the user entering the room. Each user has their own
presence, and this is readable for all other connected users. A user’s
Presence resets every time they disconnect. This object must be
JSON-serializable. [Learn more](/content/docs/api-reference/liveblocks-client#setting-initial-presence/index.html).

- options.initialStorageLsonObject

The initial Storage structure for the room when it’s joined for the first
time. This is only set a single time, when the room has not yet been
populated. This object must contain [conflict-free live\\
structures](/content/docs/api-reference/liveblocks-client#Storage/index.html). [Learn\\
more](/content/docs/api-reference/liveblocks-client#setting-initial-storage/index.html).

- options.autoConnectbooleanDefault istrue

Whether the room immediately connects to Liveblocks servers.

- options.engine1 \| 2

Preferred storage engine version to use when creating the room. Only takes
effect if the room doesn’t exist yet. The v2 Storage engine supports larger
documents, is more performant, has native streaming support, and will become
the default in the future. [Learn\\
more](/content/docs/guides/about-the-new-storage-engine/index.html).

#### [Setting initial Presence](/content/docs/api-reference/liveblocks-client\#setting-initial-presence/index.html)

Presence is used for storing temporary user-based values, such as a user’s
cursor coordinates, or their current selection. Each user has their own
presence, and this is readable for all other connected users. Set your initial
Presence value by using `initialPresence`.

```ts
const { room, leave } = client.enterRoom("my-room-id", {  initialPresence: {    cursor: null,    colors: ["red", "purple"],    selection: {      id: 72426,    },  },
  // Other options  // ...});
```

Each user’s Presence resets every time they disconnect, as this is only meant
for temporary data. Any JSON-serializable object is allowed (the `JsonObject`
type).

#### [Setting initial Storage](/content/docs/api-reference/liveblocks-client\#setting-initial-storage/index.html)

Storage is used to store permanent data that’s used in your application, such as
shapes on a whiteboard, nodes on a flowchart, or text in a form. The first time
a room is entered, you can set an initial value by using `initialStorage`.
`initialStorage` is only read and set a single time, unless a new top-level
property is added.

```ts
import { LiveList, LiveObject } from "@liveblocks/client";
const { room, leave } = client.enterRoom("my-room-id", {  initialStorage: {    title: "Untitled",    shapes: new LiveList([      new LiveObject({ type: "rectangle", color: "yellow" }),    ]),  },
  // Other options  // ...});
```

If a new top-level property is added to `initialStorage`, the next time a user
connects, the new property will be created. Other properties will be unaffected.
Any
[conflict-free live structures](/content/docs/api-reference/liveblocks-client#Storage/index.html)
and JSON-serializable objects are allowed (the `LsonObject` type).

#### [Speed up connecting to a room](/content/docs/api-reference/liveblocks-client\#speed-up-connecting-to-a-room/index.html)

To speed up connecting to a room, you can call
[`Liveblocks.prewarmRoom`](/content/docs/api-reference/liveblocks-node#get-rooms-roomId-prewarm/index.html)
on the server, which will warm up a room for the next 10 seconds. Triggering
this directly before a user navigates to a room is an easy to way use this API.

### [Client.getRoom](/content/docs/api-reference/liveblocks-client\#Client.getRoom)

Gets a room by its ID. Returns `null` if [`client.enterRoom`](/content/docs/api-reference/liveblocks-client#Client.enterRoom) has not been
called previously.

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

It’s unlikely you’ll need this API if you’re using the newer
[`client.enterRoom`](/content/docs/api-reference/liveblocks-client#Client.enterRoom) API. Note that it’s possible to
[add types to your room](/content/docs/api-reference/liveblocks-client#typing-your-data/index.html).

Returns

- roomRoom<Presence, Storage, UserMeta, RoomEvent> \| null

A [Room](/content/docs/api-reference/liveblocks-client#Room/index.html), used for building your Liveblocks application. Returns
`null` if the room has not yet been joined by the current client. Learn more
about [typing your room](/content/docs/api-reference/liveblocks-client#typing-your-data/index.html).

Arguments

- roomIdstringRequired

The ID of the room you’re connecting to.

### [Client.getSyncStatus](/content/docs/api-reference/liveblocks-client\#Client.getSyncStatus)

Gets the current Liveblocks synchronization status.

```ts
const syncStatus = client.getSyncStatus();// "synchronizing" | "synchronized"
```

Returns

- returns"synchronizing" \| "synchronized"

Will be `"synchronizing"` if there are any local changes to any part of
Liveblocks that still need to be acknowledged by the server. Will be
`"synchronized"` when all local changes have been persisted.

### [Client.logout](/content/docs/api-reference/liveblocks-client\#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.

```ts
client.logout();
```

Returns

_Nothing_

Arguments

_None_

#### [When to logout](/content/docs/api-reference/liveblocks-client\#When-to-logout/index.html)

Use this function if you have a single page application (SPA) and you wish to
log your user out, and reauthenticate them. This is a way to update your user’s
`info` after a connection has begun.

## [AI Copilots](/content/docs/api-reference/liveblocks-client\#AI-Copilots/index.html)

### [defineAiTool](/content/docs/api-reference/liveblocks-client\#defineAiTool/index.html)

Create a custom tool for your AI copilot to use. Defining tools allow the AI
copilot to look up information on-demand, render your own components based on
the tool’s arguments, or perform actions in your application on behalf of the
current user, such as creating content, updating the application state, or
interacting with external services.

```tsx
import { defineAiTool } from "@liveblocks/client";
const myTool = defineAiTool()({  description: "Fetch user information by ID",  parameters: {    type: "object",    properties: {      userId: { type: "string", description: "The user’s unique identifier" },    },    required: ["userId"],    additionalProperties: false,  },  execute: async ({ userId }) => {    const user = await getUserById(userId);    return { data: { user } };  },  render: ({ result }) => (    <AiTool title="User Lookup" icon="👤">      {!result.data ? (        <div>Looking up user...</div>      ) : (        <div>Found user: {result.data.user.name}</div>      )}    </AiTool>  ),});
```

Note that the function should be called like this `defineAiTool()({ ... })`
(double-parens). This allows TypeScript’s inference to work correctly. For the
best type inference experience, TypeScript 5.3 or higher is recommended. While
Liveblocks supports TypeScript 5.0+, full type inference for `defineAiTool()`
requires TypeScript 5.3+.

Returns

- toolAiOpaqueToolDefinition

An AI tool.

Arguments

- descriptionstringRequired

A clear description of what the tool does. Used by AI to understand when to
call this tool.

- parametersJSONSchema7Required

JSON Schema defining the tool’s input parameters. The AI will validate
arguments against this schema.

- enabledboolean

Whether this tool should be enabled. When set to `false`, the tool will not
be made available to the AI copilot for any new/future chat messages, but
will still allow existing tool invocations to be rendered that are part of
the historic chat record. Defaults to true.

- executefunction

Async function that performs the tool’s action. Receives validated arguments
and execution context, returns structured data. See [implementing the tool\\
call via `execute`](/content/docs/api-reference/liveblocks-client#implement-via-execute/index.html).

- renderfunction

React component function that renders the tool’s UI during different
execution stages. See [tool call rendering\\
stages](/content/docs/api-reference/liveblocks-client#tool-call-rendering-stages/index.html).

Tools can be registered globally with
[`RegisterAiTool`](/content/docs/api-reference/liveblocks-react#RegisterAiTool/index.html) or
passed directly to [`AiChat`](/content/docs/api-reference/liveblocks-react-ui#AiChat/index.html).

### [Tool call rendering stages](/content/docs/api-reference/liveblocks-client\#tool-call-rendering-stages/index.html)

Rendering a tool call can be done before the tool call is executed, which allows
you to display a UI during its entire lifecycle. The tool call stages are:

- `receiving` (since [3.4](/content/docs/api-reference/liveblocks-client/index.html)) The tool call is being received and its args are
being streamed in. During this stage, you can access `partialArgs` to display
a UI while the tool call arguments are still being constructed, but before the
tool call is executed.
- `executing` The tool call is currently executing, or is ready to be. In this
stage, the `args` are fully known, but the result of the tool call is not
known yet.
- `executed` The tool call has been executed, and the result is known. This
happens after your `execute` function was run, or after you called `respond()`
inside `render`. In this stage, the `result` object will be available.

The render component will automatically re-render when its stage changes.

### [Implementing tool calls](/content/docs/api-reference/liveblocks-client\#Implementing-tool-calls/index.html)

When you implement a tool call, use one of these combinations:

1. Implement `execute` _and_`render`
2. Implement only `execute`, but no `render`
3. Implement only `render`, but make sure to eventually call `respond()`

#### [Implementing your tool call via `execute`](/content/docs/api-reference/liveblocks-client\#implement-via-execute/index.html)

If you implement the `execute` function, this function will automatically be
invoked when the tool call gets made. The return value of this function will be
the result that will be passed back to the AI copilot.

- `{ data: any, description?: string }` The data to return in case of success.
`data` must be a legal JSON value. Providing a description is optional. If you
provide a description, it will be passed to the AI copilot to help it
understand the returned data or to provide follow-up instructions for how to
respond to this tool result.
- `{ error: string }` The error message in case the tool call failed to execute.
- `{ cancel: true | string }` If the tool call should be cancelled. You can
optionally provide a cancel reason as an instruction to the AI copilot.

The returned value can be observed in the `render` method, through the `result`
param:

```tsx
defineAiTool()({  /* ... */
  execute: async () => {    await sleep(1000);    return { data: { user: { name: "Alice" } } };  },
  render: ({ result }) => {    if (result.data) {      return <div>Found user: {result.data.user.name}</div>;    }
    // Tool hasn’t executed yet    return <Spinner />;  },});
```

If you do not implement `render` alongside `execute`, the tool call will still
be executed, but no UI will be displayed. The result will still be passed back
to the AI copilot.

#### [Implementing your tool call via `render`](/content/docs/api-reference/liveblocks-client\#implement-via-render/index.html)

Sometimes you may not want to immediately execute the tool call. This is most
common to build a Human-in-the-Loop (HITL) style UI where you want the user to
confirm or correct the tool call’s behavior. In these scenarios, you do not want
to implement `execute`. Instead, you could display any UI, as long as you
eventually call the `respond` function that is provided to `render`’s props.

```tsx
defineAiTool()({  /* ... */  /* NOTE: No execute method used here! */
  render: ({ respond }) => {    return (      <div>        <button          onClick={() => {            respond({ data: { user: { name: "Alice" } } });          }}        >          Confirm        </button>        <button          onClick={() => {            respond({ cancel: true });          }}        >          Cancel        </button>      </div>    );  },});
```

In this example, until the Confirm button is clicked, the AI chat will remain in
“executing” stage, awaiting the result of this tool call.

This example is for illustrative purposes only. In practice, using our
[`AiTool.Confirmation`](/content/docs/api-reference/liveblocks-react-ui#AiTool.Confirmation)
tool is preferred for building confirm/cancel flows.

Like with the `execute` function, the `respond` function should be called with a
value of this shape:

#### [Handling different tool call stages](/content/docs/api-reference/liveblocks-client\#handling-stages/index.html)

You can handle all three stages of a tool call in your render function to
provide a smooth user experience during tool call streaming and execution:

```tsx
const bookFlightTool = defineAiTool()({  description: "Book a flight for a user",  parameters: {    type: "object",    properties: {      origin: { type: "string", description: "Departure city" },      destination: { type: "string", description: "Arrival city" },      departureDate: {        type: "string",        description: "Departure date (YYYY-MM-DD)",      },      passengers: {        type: "array",        items: {          type: "object",          properties: {            name: { type: "string" },            age: { type: "number" },          },          required: ["name", "age"],          additionalProperties: false,        },        description: "List of passengers",      },    },    required: ["origin", "destination", "departureDate", "passengers"],    additionalProperties: false,  },  execute: async ({ origin, destination, departureDate, passengers }) => {    const booking = await bookFlight({      origin,      destination,      departureDate,      passengers,    });    return { data: { bookingId: booking.id } };  },  render: ({ stage, partialArgs, args, result }) => {    return (      <AiTool title="Flight Booking" icon="✈️">        {stage === "receiving" && (          <div>            <h4>Preparing flight booking...</h4>            {partialArgs.origin && <p>From: {partialArgs.origin}</p>}            {partialArgs.destination && <p>To: {partialArgs.destination}</p>}            {partialArgs.departureDate && (              <p>Date: {partialArgs.departureDate}</p>            )}            {partialArgs.passengers && (              <div>                <p>Passengers ({partialArgs.passengers.length}):</p>                <ul>                  {partialArgs.passengers.map((passenger, index) => (                    <li key={index}>                      {passenger?.name || "Loading..."}                      {passenger?.age && ` (${passenger.age})`}                    </li>                  ))}                </ul>              </div>            )}          </div>        )}        {stage === "executing" && (          <div>            <h4>Booking flight...</h4>            <p>              {args.origin} → {args.destination} on {args.departureDate}            </p>            <p>{args.passengers.length} passenger(s)</p>          </div>        )}        {stage === "executed" && result.data && (          <div>            <h4>Flight booked successfully!</h4>            <p>Booking ID: {result.data.bookingId}</p>          </div>        )}      </AiTool>    );  },});
```

In this example, the tool arguments stream in progressively during the
`receiving` stage, causing multiple re-renders as each field appears:

- **1st render**: `{ stage: "receiving", partialArgs: {} }`
- **2nd render**: `{ stage: "receiving", partialArgs: { origin: "New York" } }`
- **3rd render**:
`{ stage: "receiving", partialArgs: { origin: "New York", destination: "London" } }`
- **4th render**:
`{ stage: "receiving", partialArgs: { origin: "New York", destination: "London", departureDate: "2024-12-15" } }`
- **5th render**: `{ stage: "receiving", partialArgs: { ..., passengers: [] } }`
- **6th render**:
`{ stage: "receiving", partialArgs: { ..., passengers: [{ name: "John" }] } }`
- **7th render**:
`{ stage: "receiving", partialArgs: { ..., passengers: [{ name: "John", age: 3 }] } }`
- **8th render**:
`{ stage: "receiving", partialArgs: { ..., passengers: [{ name: "John", age: 30 }] } }`
- **Final render**: `{ stage: "executing", args: { /* complete object */ } }`

This demonstrates how each field and nested property appears incrementally,
providing real-time feedback to users as the AI constructs the tool call
arguments.

Arguments are streamed in forward-only order. Once a field begins appearing, all
previous fields are complete and won’t be modified. You’ll never see
`{ origin: "New York", destination: "London" }` followed by
`{ origin: "San Francisco", destination: "London" }`, but you might see
`{ origin: "New" }` then `{ origin: "New York" }` then
`{ origin: "New York", destination: "London" }`.

## [Room](/content/docs/api-reference/liveblocks-client\#Room/index.html)

Room returned by [`client.enterRoom`](/content/docs/api-reference/liveblocks-client#Client.enterRoom) (or [`client.getRoom`](/content/docs/api-reference/liveblocks-client#Client.getRoom)).

### [Room.getPresence](/content/docs/api-reference/liveblocks-client\#Room.getPresence)

Return the current user’s Presence.
[Presence](/content/docs/ready-made-features/presence/index.html) is used to store custom
properties on each user that exist until the user disconnects. An example use
would be storing a user’s cursor coordinates.

```ts
const presence = room.getPresence();
// { cursor: { x: 363, y: 723 } }console.log(presence);
```

Presence is set with [`updatePresence`](/content/docs/api-reference/liveblocks-client#Room.updatePresence) and can be typed
when you [enter a room](/content/docs/api-reference/liveblocks-client#enter-room-typing-a-room/index.html). The example above is using
the following type:

liveblocks.config.ts

```ts
declare global {  interface Liveblocks {    Presence: {      cursor: { x: number; y: number };    };  }}
```

Returns

- presenceTPresence

An object holding the Presence value for the currently connected user.
Presence is set with [`updatePresence`](/content/docs/api-reference/liveblocks-client#Room.updatePresence). Will always
be JSON-serializable. `TPresence` is the `Presence` type you set yourself,
[learn more](/content/docs/api-reference/liveblocks-client#Typing-presence/index.html).

Arguments

_None_

### [Room.updatePresence](/content/docs/api-reference/liveblocks-client\#Room.updatePresence)

Updates the current user’s [Presence](/content/docs/ready-made-features/presence/index.html). 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.

```ts
room.updatePresence({ typing: true });room.updatePresence({ status: "Online" });
// { typing: true, status: "Online" }const presence = room.getPresence();
```

Returns

_Nothing_

Arguments

- updateTPresenceRequired

The updated Presence properties for the current user inside an object. The
user’s entire Presence object will not be replaced, instead these properties
will be merged with the existing Presence. This object must be
JSON-serializable.

- options.addToHistorybooleanDefault isfalse

Adds Presence values to the history stack, meaning using undo and redo
functions will change them. [Learn more](/content/docs/api-reference/liveblocks-client#add-presence-to-history/index.html).

#### [Add Presence to history](/content/docs/api-reference/liveblocks-client\#add-presence-to-history/index.html)

By default, Presence values are not added to history. However, using the
`addToHistory` option will add items to the undo/redo stack.

```ts
room.updatePresence({ color: "blue" }, { addToHistory: true });room.updatePresence({ color: "red" }, { addToHistory: true });room.history.undo();
// { color: "blue" }const presence = room.getPresence();
```

See [`room.history`](/content/docs/api-reference/liveblocks-client#Room.history) for more information.

### [Room.getOthers](/content/docs/api-reference/liveblocks-client\#Room.getOthers)

Returns an array of currently connected users in the room. Returns a
[`User`](/content/docs/api-reference/liveblocks-client#user-type/index.html) object for each user. Note that you can also subscribe to
others using [`Room.subscribe("others")`](/content/docs/api-reference/liveblocks-client#Room.subscribe.others).

```ts
const others = room.getOthers();
for (const other of others) {  const { connectionId, id, info, presence, canWrite, canComment } = other;  // Do things}
```

Returns

- othersUser<Presence, UserMeta>\[\]

An array holding each connected user’s [`User`](/content/docs/api-reference/liveblocks-client#user-type/index.html) object. `User`
contains the current user’s Presence value, along with other information.
Presence is set with [`updatePresence`](/content/docs/api-reference/liveblocks-client#Room.updatePresence). Returns an
empty array when no other users are currently connected. Will always be
JSON-serializable.

Arguments

_None_

### [Room.broadcastEvent](/content/docs/api-reference/liveblocks-client\#Room.broadcastEvent)

Broadcast an event to other users in the Room. Events broadcast to the room can
be listened to with [`Room.subscribe("event")`](/content/docs/api-reference/liveblocks-client#Room.subscribe.event). Takes a custom event payload
as first argument. Should be serializable to JSON.

```ts
room.broadcastEvent({ type: "REACTION", emoji: "🔥" });
```

Returns

_Nothing_

Arguments

- eventTRoomEventRequired

The event to broadcast to every other user in the room. Must be
JSON-serializable. `TRoomEvent` is the `RoomEvent` type you set yourself,
[learn more](/content/docs/api-reference/liveblocks-client#typing-multiple-events/index.html).

- options.shouldQueueEventIfNotReadybooleanDefault isfalse

Queue the event if the connection is currently closed, or has not been
opened yet. We’re not sure if we want to support this option in the future
so it might be deprecated to be replaced by something else. [Learn\\
more](/content/docs/api-reference/liveblocks-client#broadcasting-an-event-when-disconnected/index.html).

#### [Receiving an event](/content/docs/api-reference/liveblocks-client\#Receiving-an-event/index.html)

To receive an event, use [`Room.subscribe("event")`](/content/docs/api-reference/liveblocks-client#Room.subscribe.event). The `user` property
received on the other end is the sender’s [`User`](/content/docs/api-reference/liveblocks-client#user-type/index.html) instance.

```ts
// User 1room.broadcastEvent({ type: "REACTION", emoji: "🔥" });
// User 2const unsubscribe = room.subscribe("event", ({ event, user, connectionId }) => {  //                                                  ^^^^ User 1  if (event.type === "REACTION") {    // Do something  }});
```

We recommend using a property such as `type`, so that it’s easy to distinguish
between different events on the receiving end.

#### [Typing multiple events](/content/docs/api-reference/liveblocks-client\#typing-multiple-events/index.html)

When [defining your types](/content/docs/api-reference/liveblocks-client#typing-your-data/index.html), you can pass a `RoomEvent` type
in your config file to receive type hints in your app. To define multiple
different custom events, use a union.

```ts
declare global {  interface Liveblocks {    RoomEvent:      | { type: "REACTION"; emoji: string }      | { type: "ACTION"; action: string };  }}
```

```ts
room.subscribe("event", ({ event, user, connectionId }) => {  if (event.type === "REACTION") {    // Do something  }  if (event.type === "ACTION") {    // Do something else  }});
```

#### [Broadcasting an event when disconnected](/content/docs/api-reference/liveblocks-client\#broadcasting-an-event-when-disconnected/index.html)

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.

We’re not sure if we want to support `shouldQueueEventIfNotReady` in the future,
so it may be deprecated and replaced with something else.

```ts
room.broadcastEvent(  { type: "REACTION", emoji: "🔥" },  {    shouldQueueEventIfNotReady: true,  });
```

### [Room.getSelf](/content/docs/api-reference/liveblocks-client\#Room.getSelf)

Gets the current [`User`](/content/docs/api-reference/liveblocks-client#user-type/index.html). Returns `null` if the client is not yet
connected to the room.

```ts
const { connectionId, presence, id, info, canWrite, canComment } =  room.getSelf();
```

Returns

- userUser<Presence, UserMeta> \| null

Returns the current [`User`](/content/docs/api-reference/liveblocks-client#user-type/index.html). Returns `null` if the client is
not yet connected to the room.

Arguments

_None_

Here’s an example of a full return value, assuming `Presence` and `UserMeta` [have been set](/content/docs/api-reference/liveblocks-client#user-type/index.html).

```ts
const user = room.getSelf();
// {//   connectionId: 52,//   presence: {//     cursor: { x: 263, y: 786 },//   },//   id: "mislav.abha@example.com",//   info: {//     avatar: "/mislav.png",//   },//   canWrite: true,//   canComment: true,// }console.log(user);
```

### [Room.getStatus](/content/docs/api-reference/liveblocks-client\#Room.getStatus)

Gets the current WebSocket connection status of the room. The possible value
are: `initial`, `connecting`, `connected`, `reconnecting`, or `disconnected`.

```ts
const status = room.getStatus();
// "connected"console.log(status);
```

Returns

- status"initial" \| "connecting" \| "connected" \| "reconnecting" \| "disconnected"

Returns the room’s current connection status. It can return one of five values:

- `"initial"` The room has not attempted to connect yet.
  - `"connecting"` The room is currently authenticating or connecting.
  - `"connected"` The room is connected.
  - `"reconnecting"` The room has disconnected, and is trying to connect again.
  - `"disconnected"` The room is disconnected, and is no longer attempting to connect.

Arguments

_None_

### [Room.getStorageStatus](/content/docs/api-reference/liveblocks-client\#Room.getStorageStatus)

Get the Storage status. Use this to tell whether Storage has been synchronized
with the Liveblocks servers.

```ts
const status = room.getStorageStatus();
// "synchronizing"console.log(status);
```

Returns

- status"not-loaded" \| "loading" \| "synchronizing" \| "synchronized"

The current room’s Storage status. `status` can be one of four types.

- `"not-loaded"` Storage has not been loaded yet as [`room.getStorage`](/content/docs/api-reference/liveblocks-client#Room.getStorage) has not been called.
  - `"loading"` Storage is currently loading for the first time.
  - `"synchronizing"` Local Storage changes are currently being synchronized.
  - `"synchronized"` Local Storage changes have been synchronized.

Arguments

_None_

### [Room.subscribe(storageItem)](/content/docs/api-reference/liveblocks-client\#Room.subscribe(storageItem/index.html))

Subscribe to updates on a particular storage item, and takes a callback function
that’s called when the storage item is updated. The Storage `root` is a
[`LiveObject`](/content/docs/api-reference/liveblocks-client#LiveObject/index.html), which means you can subscribe to this, as well as other live
structures. Returns an unsubscribe function.

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

Returns

- unsubscribe() =\> void

Unsubscribe function. Call it to cancel the subscription.

Arguments

- storageItemL extends (LiveObject \| LiveMap \| LiveList)Required

The `LiveObject`, `LiveMap`, or `LiveList` which is being subscribed to.
Each time the structure is updated, the callback is called.

- callback(node: L) => voidRequired

Function that’s called when `storageItem` updates. Returns the updated
storage structure.

- options.isDeepboolean

Subscribe to both `storageItem` and its children. The callback function will
be passed a list of updates instead of just the new Storage item. [Learn\\
more](/content/docs/api-reference/liveblocks-client#listening-for-nested-changes/index.html).

#### [Typing Storage](/content/docs/api-reference/liveblocks-client\#Typing-Storage/index.html)

To type the Storage values you receive, make sure to set your `Storage` type.

liveblocks.config.ts

```ts
import { LiveList } from "@liveblocks/client";
declare global {  interface Liveblocks {    Storage: {      animals: LiveList<{ name: string }>;    };  }}
```

The type received in the callback will match the type passed. Learn more under
[typing your room](/content/docs/api-reference/liveblocks-client#typing-your-data/index.html).

```ts
const { root } = await room.getStorage();const animals = root.get("animals");
const unsubscribe = room.subscribe(animals, (updatedAnimals) => {  // LiveList<[{ name: "Fido" }, { name: "Felix" }]>  console.log(updatedAnimals);});
```

#### [Subscribe to any live structure](/content/docs/api-reference/liveblocks-client\#Subscribe-to-any-live-structure/index.html)

You can subscribe to any live structure, be it the Storage `root`, a child, or a
structure even more deeply nested.

liveblocks.config.ts

```ts
import { LiveMap, LiveObject } from "@liveblocks/client";
type Person = LiveObject<{ name: string; age: number }>;
declare global {  interface Liveblocks {    Storage: {      people: LiveMap<string, Person>;    };  }}
```

```ts
const { root } = await room.getStorage();const people = root.get("people");const steven = people.get("steven");
const unsubscribeRoot = room.subscribe(root, (updatedRoot) => {  // ...});
const unsubscribePeople = room.subscribe(people, (updatedPeople) => {  // ...});
const unsubscribeSteven = room.subscribe(steven, (updatedSteven) => {  // ...});
```

#### [Listening for nested changes](/content/docs/api-reference/liveblocks-client\#listening-for-nested-changes/index.html)

It’s also possible to subscribe to a Storage item and all of its children by
passing an optional `isDeep` option in the third argument. In this case, the
callback will be passed a list of updates instead of just the new Storage item.
Each such update is a `{ type, node, updates }` object.

```ts
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 });
```

#### [Using async functions](/content/docs/api-reference/liveblocks-client\#Using-async-functions/index.html)

You use an `async` function inside the subscription callback, though bear in
mind that the callback itself is synchronous, and there’s no guarantee the
`async` function will complete before the callback is run again.

```ts
const { root } = await room.getStorage();
const unsubscribe = room.subscribe(root, (updatedRoot) => {  async function doThing() {    await fetch(/* ... */);  }
  doThing();});
```

If the order of updates is important in your application, and it’s important to
ensure that your `async` function doesn’t start before the previous one
finishes, you can use a package such as
[`async-mutex`](https://www.npmjs.com/package/async-mutex) to help you with
this. Using `runExclusive` will effectively form a queue for all upcoming
updates, guaranteeing serial execution.

```ts
import { Mutex } from "async-mutex";
const { root } = await room.getStorage();const myMutex = new Mutex();
const unsubscribeUpdates = room.subscribe(root, (root) => {  void myMutex.runExclusive(async () => {    await fetch(/* ... */);  });});
```

Note that this may cause a performance penalty in your application, as certain
updates will be ignored.

### [Room.subscribe("event")](/content/docs/api-reference/liveblocks-client\#Room.subscribe.event)

Subscribe to events broadcast by [`Room.broadcastEvent`](/content/docs/api-reference/liveblocks-client#Room.broadcastEvent). Takes a callback
that’s run when another user calls [`Room.broadcastEvent`](/content/docs/api-reference/liveblocks-client#Room.broadcastEvent). Provides the
`event` along with the `user` and their `connectionId` of the user that sent the
message. Returns an unsubscribe function.

```ts
// User 1room.broadcastEvent({ type: "REACTION", emoji: "🔥" });
// User 2const unsubscribe = room.subscribe("event", ({ event, user, connectionId }) => {  //                                                  ^^^^ Will be User 1  if (event.type === "REACTION") {    // Do something  }});
```

Returns

- unsubscribe() =\> void

Unsubscribe function. Call it to cancel the subscription.

Arguments

- eventType"event"Required

Listen to events.

- callbackSee full typeRequired

Function that’s called when another user sends an event. Receives the event,
the [`user`](/content/docs/api-reference/liveblocks-client#user-type/index.html) that sent the event, and their `connectionId`. If
this event was sent via
[`liveblocks.broadcastEvent`](/content/docs/api-reference/liveblocks-node#post-broadcast-event/index.html)
or the [Broadcast event\\
API](/content/docs/api-reference/rest-api-endpoints#post-broadcast-event/index.html), `user`
will be `null` and `connectionId` will be `-1`. [Learn\\
more](/content/docs/api-reference/liveblocks-client#receiving-events-from-the-server/index.html)

#### [Typing events](/content/docs/api-reference/liveblocks-client\#Typing-events/index.html)

When [defining your types](/content/docs/api-reference/liveblocks-client#typing-your-data/index.html), you can pass a `RoomEvent` type
to your config file to receive type hints in your app. To define multiple
different custom events, use a union.

```ts
declare global {  interface Liveblocks {    RoomEvent:      | { type: "REACTION"; emoji: string }      | { type: "ACTION"; action: string };  }}
```

#### [Receiving events from the server](/content/docs/api-reference/liveblocks-client\#receiving-events-from-the-server/index.html)

Events can be received from the server with either
[`liveblocks.broadcastEvent`](/content/docs/api-reference/liveblocks-node#post-broadcast-event/index.html)
or the
[Broadcast Event API](/content/docs/api-reference/rest-api-endpoints#post-broadcast-event/index.html).
In events sent from the server, `user` will be `null`, and `connectionId` will
be `-1`.

[Sign in](/content/dashboard/apikeys/index.html) to get your own API keys.

```ts
import { Liveblocks } from "@liveblocks/node";
const liveblocks = new Liveblocks({  secret: "sk_prod_xxxxxxxxxx…xxxxxxxxsk_prod_xxxxxxxxxxxxxxxxxxxxxxxx",});
export async function POST() {  await liveblocks.broadcastEvent({ type: "REACTION", emoji: "🔥" });}
```

```ts
const unsubscribe = room.subscribe("event", ({ event, user, connectionId }) => {  // `null`, `-1`  console.log(user, connectionId);});
```

### [Room.subscribe("my-presence")](/content/docs/api-reference/liveblocks-client\#Room.subscribe.my-presence/index.html)

Subscribe to the current user’s Presence. Takes a callback that is called every
time the current user presence is updated with [`Room.updatePresence`](/content/docs/api-reference/liveblocks-client#Room.updatePresence).
Returns an unsubscribe function.

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

Returns

- unsubscribe() =\> void

Unsubscribe function. Call it to cancel the subscription.

Arguments

- eventType"my-presence"Required

Listen to the current user’s presence.

- callback(presence: TPresence) => voidRequired

Function that’s called when the current user’s Presence has updated, for
example with [`Room.updatePresence`](/content/docs/api-reference/liveblocks-client#Room.updatePresence). Receives the updates Presence value.

#### [Typing Presence](/content/docs/api-reference/liveblocks-client\#Typing-Presence/index.html)

To type the Presence values you receive, make sure to set your Presence type.

liveblocks.config.ts

```ts
declare global {  interface Liveblocks {    Presence: {      status: string;      cursor: { x: number; y: number };    };  }}
```

The type received in the callback will match the type passed. Learn more under
[typing your data](/content/docs/api-reference/liveblocks-client#typing-your-data/index.html).

```ts
const unsubscribe = room.subscribe("my-presence", (presence) => {  // { status: "typing", cursor: { x: 45, y: 67 }  console.log(presence);});
```

### [Room.subscribe("others")](/content/docs/api-reference/liveblocks-client\#Room.subscribe.others)

Subscribe to every other users’ updates. Takes a callback that’s called when a
user’s Presence updates, or when they enter or leave the room. Returns an
unsubscribe function.

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

Returns

- unsubscribe() =\> void

Unsubscribe function. Call it to cancel the subscription.

Arguments

- eventType"others"Required

Listen to others.

- callback(others: User<Presence, UserMeta>\[\], event: OthersEvent) => voidRequired

Function that’s called when another user’s Presence has updated, for example
with [`Room.updatePresence`](/content/docs/api-reference/liveblocks-client#Room.updatePresence), or an others event has occurred. Receives an
array of [`User`](/content/docs/api-reference/liveblocks-client#user-type/index.html) values for each currently connected user. Also
received an object with information about the event that has triggered the
update, [learn more](/content/docs/api-reference/liveblocks-client#listening-for-others-events/index.html).

#### [Typing Presence](/content/docs/api-reference/liveblocks-client\#Typing-Presence/index.html)

To type the Presence values you receive, make sure to set your Presence type.

liveblocks.config.ts

```ts
declare global {  interface Liveblocks {    Presence: {      status: string;      cursor: { x: number; y: number };    };  }}
```

```ts
const unsubscribe = room.subscribe("others", (others, event) => {  // { status: "typing", cursor: { x: 45, y: 67 }  console.log(others[0].presence);});
```

#### [Listening for others events](/content/docs/api-reference/liveblocks-client\#listening-for-others-events/index.html)

The `event` parameter returns information on why the callback has just run, for
example if their Presence has updated, if they’ve just left or entered the room,
or if the current user has disconnected.

```ts
const unsubscribe = room.subscribe("others", (others, event) => {  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  }});
```

#### [Live cursors](/content/docs/api-reference/liveblocks-client\#Live-cursors/index.html)

Here’s a basic example showing you how to render live cursors.
[`Room.updatePresence`](/content/docs/api-reference/liveblocks-client#Room.updatePresence)
is being used to update each user’s cursor position.

liveblocks.config.ts

```ts
declare global {  interface Liveblocks {    Presence: {      cursor: { x: number; y: number };    };  }}
```

```ts
const { room, leave } = client.enterRoom("my-room-id");
// Call this to update the current user’s Presencefunction updateCursorPosition({ x, y }) {  room.updatePresence({ cursor: { x, y } });}
const others = room.getOthers();
// Run __renderCursor__ when any other connected user updates their presenceconst unsubscribe = room.subscribe("others", (others, event) => {  for (const { id, presence } of others) {    const { x, y } = presence.cursor;    __renderCursor__(id, { x, y });  }}
// Handle events and rendering// ...
```

Check our [examples page](/content/examples/browse/cursors/index.html) for live demos.

### [Room.subscribe("status")](/content/docs/api-reference/liveblocks-client\#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.

```ts
const unsubscribe = room.subscribe("status", (status) => {  // "connected"  console.log(status);});
```

Returns

- unsubscribe() =\> void

Unsubscribe function. Call it to cancel the subscription.

Arguments

- eventType"status"Required

Listen to status updates.

- callbackSee full typeRequired

Function that’s called when the room’s connection status has changed. It can return one of five values:

#### [When to use status](/content/docs/api-reference/liveblocks-client\#When-to-use-status/index.html)

Status 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
with quick `connected` → `reconnecting` → `connected` status jumps.

```ts
let indicator = "⚪";
const unsubscribe = room.subscribe("status", (status) => {  switch (status) {    case "connecting":      indicator = "🟡";      break;    case "connected":      indicator = "🟢";      break;    // ...  }});
```

If you’d like to let users know that there may be connectivity issues, don’t use
this API, but instead refer to [`Room.subscribe("lost-connection")`](/content/docs/api-reference/liveblocks-client#Room.subscribe.lost-connection/index.html) which was
specially built for this purpose.

Do not use this API to detect when Storage or Presence are initialized or
loaded. "Connected" does not guarantee that Storage or Presence are ready. To
detect when Storage is loaded, rely on awaiting the [`Room.getStorage`](/content/docs/api-reference/liveblocks-client#Room.getStorage)
promise or using the [`Room.subscribe("storage-status")`](/content/docs/api-reference/liveblocks-client#Room.subscribe.storage-status/index.html) event.

### [Room.subscribe("lost-connection")](/content/docs/api-reference/liveblocks-client\#Room.subscribe.lost-connection/index.html)

A special-purpose event that will fire when a previously connected Liveblocks
client has lost connection, for example due to a network outage, and was unable
to recover quickly. This event is
[designed to help improve UX for your users](/content/docs/api-reference/liveblocks-client#when-to-use-lost-connection-events/index.html),
and will not trigger on short interruptions, those that are less than
[5 seconds by default](/content/docs/api-reference/liveblocks-client#setting-lost-connection-timeout/index.html). The event only
triggers if a previously connected client disconnects.

```ts
const unsubscribe = room.subscribe("lost-connection", (event) => {  // "lost"  console.log(event);});
```

Returns

- unsubscribe() =\> void

Unsubscribe function. Call it to cancel the subscription.

Arguments

- eventType"lost-connection"Required

Listen to lost connection events.

- callbackSee full typeRequired

Function that’s called when a room’s lost connection event has been triggered. It can return one of three values:

- `"lost"` A connection has been lost for longer than [`lostConnectionTimeout`](/content/docs/api-reference/liveblocks-client#createClientLostConnectionTimeout/index.html).
  - `"restored"` The connection has been restored again.
  - `"failed"` The room has been unable to reconnect again, and is no longer trying. This may happen if a user’s
    network has recovered, but the room’s authentication values no longer allow them to enter.

#### [When to use lost connection events](/content/docs/api-reference/liveblocks-client\#when-to-use-lost-connection-events/index.html)

Lost connections events allows you to build high-quality UIs by warning your
users that the application 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).

```ts
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;  }});
```

#### [Setting lost connection timeout](/content/docs/api-reference/liveblocks-client\#setting-lost-connection-timeout/index.html)

The [`lostConnectionTimeout`](/content/docs/api-reference/liveblocks-client#createClientLostConnectionTimeout/index.html) configuration option will determine how quickly
the event triggers after a connection loss occurs. By default, it’s set to
`5000`ms, which is 5 seconds.

```ts
import { createClient } from "@liveblocks/client";
const client = createClient({  // Throw lost-connection event after 5 seconds offline  lostConnectionTimeout: 5000,
  // ...});
```

### [Room.subscribe("error")](/content/docs/api-reference/liveblocks-client\#Room.subscribe.error)

Subscribe to unrecoverable room connection errors. This event will be emitted
immediately before the client disconnects and won’t try reconnecting again.
Returns an unsubscribe function. If you’d like to retry connecting, call
[`room.reconnect`](/content/docs/api-reference/liveblocks-client#Room.reconnect).

```ts
const unsubscribe = room.subscribe("error", (error) => {  switch (error.context.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;  }});
```

Returns

- unsubscribe() =\> void

Unsubscribe function. Call it to cancel the subscription.

Arguments

- eventType"error"Required

Listen to error events.

- callbackSee full typeRequired

Function that’s called when an unrecoverable error event has been triggered. `error.code` can return one of these
values:

- `-1` Authentication error.
  - `4001` Could not connect because you don’t have access to this room.
  - `4005` Could not connect because room was full.
  - `4006` The room ID has changed.

#### [When to use error events](/content/docs/api-reference/liveblocks-client\#When-to-use-error-events/index.html)

You can use this event to trigger a “Not allowed” screen/dialog. It can also be
helpful for implementing a redirect to another page.

```ts
const unsubscribe = room.subscribe("error", (error) => {  // Could not connect because you don’t have access to this room  if (error.context.code === 4001)    return __displayForbiddenEntryDialog__();  }});
```

#### [When a room ID has changed](/content/docs/api-reference/liveblocks-client\#When-a-room-ID-has-changed/index.html)

When a room ID has been changed with
[`liveblocks.updateRoomId`](/content/docs/api-reference/liveblocks-node#post-rooms-update-roomId/index.html)
or the
[Update Room ID API](/content/docs/api-reference/rest-api-endpoints#post-rooms-update-roomId/index.html),
`error.message` will contain the new room ID.

```ts
const unsubscribe = room.subscribe("error", (error) => {  // The room ID has changed, get the new room ID  if (error.context.code === 4006)    const newRoomId = error.message;    return __redirect__(`/app/${newRoomId}`)  }});
```

### [Room.subscribe("history")](/content/docs/api-reference/liveblocks-client\#Room.subscribe.history)

Subscribe to the current user’s history changes. Returns an unsubscribe
function.

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

Returns

- unsubscribe() =\> void

Unsubscribe function. Call it to cancel the subscription.

Arguments

- eventType"history"Required

Listen to history events.

- callbackSee full typeRequired

Function that’s called when the current user’s history changes. Returns
booleans that describe whether the user can use
[undo](/content/docs/api-reference/liveblocks-client#Room.history.undo) or
[redo](/content/docs/api-reference/liveblocks-client#Room.history.redo).

### [Room.subscribe("storage-status")](/content/docs/api-reference/liveblocks-client\#Room.subscribe.storage-status/index.html)

Subscribe to Storage status changes. Use this to tell whether Storage has been
synchronized with the Liveblocks servers. Returns an unsubscribe function.

```ts
const unsubscribe = room.subscribe("storage-status", (status) => {  switch (status) {    case "not-loaded":      // Storage has not been loaded yet      break;    case "loading":      // Storage is currently loading      break;    case "synchronizing":      // Local Storage changes are being synchronized      break;    case "synchronized":      // Local Storage changes have been synchronized      break;  }});
```

Returns

- unsubscribe() =\> void

Unsubscribe function. Call it to cancel the subscription.

Arguments

- eventType"storage-status"Required

Listen to Storage status events.

- callbackSee full typeRequired

Function that’s called when the current user’s Storage updated status have
changed. `status` can be one of four types.

- `"not-loaded` \- Storage has not been loaded yet as \[`getStorage`\]\[\] has not been called.
  - `"loading"` \- Storage is currently loading for the first time.
  - `"synchronizing"` \- Local Storage changes are currently being synchronized.
  - `"synchronized"` \- Local Storage changes have been synchronized

### [Room.batch](/content/docs/api-reference/liveblocks-client\#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. When undoing or
redoing these changes, the entire batch will be undone/redone together instead
of atomically.

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

Returns

- returnT

Returns the return value from the callback.

Arguments

- callback() =\> TRequired

A callback containing every Storage and Presence notification that will be
part of the batch. Cannot be an `async` function.

#### [When to batch updates](/content/docs/api-reference/liveblocks-client\#When-to-batch-updates/index.html)

For the most part, _you don’t need to batch updates_. For example, given a
[whiteboard application](/content/examples/browse/whiteboard/index.html), it’s perfectly fine to
update a note’s position on the board multiple times per second, in separate
updates. However, should you implement a “Delete all” button, that may delete 50
notes at once, this is where you should use a batch.

```ts
const { root } = await room.getStorage();const notes = root.get("notes");
// ✅ Batch simultaneous changes togetherroom.batch(() => {  for (const noteId of notes.keys()) {    notes.delete(noteId);  }});
```

This batch places each
[`LiveMap.delete`](/content/docs/api-reference/liveblocks-client#LiveMap.delete) call
into a single WebSocket update, instead of sending multiple updates. This will
be much quicker.

#### [Batching groups history changes](/content/docs/api-reference/liveblocks-client\#Batching-groups-history-changes/index.html)

Batching changes will also group changes into a single history state.

```ts
const { root } = await room.getStorage();const pet = root.set("pet", new LiveObject({ name: "Fido", age: 5 }));
// ✅ Batch groups changes into oneroom.batch(() => {  pet.set("name", "Felix");  pet.set("age", 10);});
// { name: "Felix", age: 10 }pet.toJSON();
room.history.undo();
// { name: "Fido", age: 5 }pet.toJSON();
```

#### [Doesn’t work with async functions](/content/docs/api-reference/liveblocks-client\#Doesn't-work-with-async-functions/index.html)

Note that `room.batch` cannot take an `async` function.

```tsx
// ❌ Won’t workroom.batch(async () => {  // ...});
// ✅ Will workroom.batch(() => {  // ...});
```

### [Room.history](/content/docs/api-reference/liveblocks-client\#Room.history)

Room’s history contains functions that let you undo and redo operations made to
Storage and Presence on the current client. Each user has a separate history
stored in memory, and history is reset when the page is reloaded.

```ts
const { undo, redo, pause, resume /*, ... */ } = room.history;
```

History in Yjs

Note that to undo or redo in Yjs, you must use a separate history manager,
[`Y.UndoManager`](https://docs.yjs.dev/api/undo-manager).

#### [Add Presence to history](/content/docs/api-reference/liveblocks-client\#Add-Presence-to-history/index.html)

By default, history is only enabled for Storage. However, you can use the
`addToHistory` option to additionally
[add Presence state to history](/content/docs/api-reference/liveblocks-client#add-presence-to-history/index.html).

```tsx
room.updatePresence({ color: "blue" }, { addToHistory: true });
```

### [Room.history.undo](/content/docs/api-reference/liveblocks-client\#Room.history.undo)

Reverts the last operation. It does not impact operations made by other clients,
and will only undo changes made by the current client.

```ts
const person = new LiveObject();person.set("name", "Pierre");person.set("name", "Jonathan");
room.history.undo();
// "Pierre"root.get("name");
```

Returns

_Nothing_

Arguments

_None_

### [Room.history.redo](/content/docs/api-reference/liveblocks-client\#Room.history.redo)

Restores the last undone operation. It does not impact operations made by other
clients, and will only restore changes made by the current client.

```ts
const person = new LiveObject();person.set("name", "Pierre");person.set("name", "Jonathan");
room.history.undo();room.history.redo();
// "Jonathan"root.get("name");
```

Returns

_Nothing_

Arguments

_None_

### [Room.history.canUndo](/content/docs/api-reference/liveblocks-client\#Room.history.canUndo)

Returns true or false, depending on whether there are any operations to undo.
Helpful for disabling undo buttons.

```ts
const person = new LiveObject();person.set("name", "Pierre");
// trueroom.history.canUndo();
room.history.undo();
// falseroom.history.canUndo();
```

Returns

- canUndoboolean

Whether there is an undo operation in the current history stack.

Arguments

_None_

### [Room.history.canRedo](/content/docs/api-reference/liveblocks-client\#Room.history.canRedo)

Returns true or false, depending on whether there are any operations to redo.
Helpful for disabling redo buttons.

```ts
const person = new LiveObject();person.set("name", "Pierre");
// falseroom.history.canRedo();
room.history.undo();
// trueroom.history.canRedo();
```

Returns

- canRedoboolean

Whether there is a redo operation in the current history stack.

Arguments

_None_

### [Room.history.clear](/content/docs/api-reference/liveblocks-client\#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.

```ts
const person = new LiveObject();person.set("name", "Pierre");
// trueroom.history.canUndo();
room.history.clear();
// falseroom.history.canUndo();
```

Returns

_Nothing_

Arguments

_None_

### [Room.history.pause](/content/docs/api-reference/liveblocks-client\#Room.history.pause)

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

```ts
const info = new LiveObject({ time: "one" });
room.history.pause();info.set("time", "two");info.set("time", "three");room.history.resume();
room.history.undo();
// "one"room.get("time");
```

Returns

_Nothing_

Arguments

_None_

### [Room.history.resume](/content/docs/api-reference/liveblocks-client\#Room.history.resume)

Resumes history after a [pause](/content/docs/api-reference/liveblocks-client#Room.history.pause). Modifications made on the
Room are not merged into a single history item any more.

Returns

_Nothing_

Arguments

_None_

### [Room.history.disable](/content/docs/api-reference/liveblocks-client\#Room.history.disable)

Experimental API

This API is experimental and may change or be removed in a future release
without following semver guarantees.

Executes a callback with history tracking temporarily disabled. Any storage
mutations made inside the callback will be applied normally but will not appear
on the undo/redo stacks. This is useful for background writes that should not be
undoable, such as writing back results from agent updates or reconciling state
from an external source.

If the callback throws, the undo/redo stacks are left unchanged (as if the
callback never ran).

```ts
room.history.disable(() => {  root.set("generatedText", result);});
```

#### [Batching](/content/docs/api-reference/liveblocks-client\#Batching/index.html)

When combining with [`room.batch`](/content/docs/api-reference/liveblocks-client#Room.batch), always place the batch _inside_
the `disable` call. If `batch` wraps `disable`, the batched mutations will still
end up on the undo stack.

```ts
// ✅ Disabling undo must happen around a batchroom.history.disable(() => {  room.batch(() => {    root.set("x", 1);    root.set("y", 2);  });});
// ❌ Batch wraps disable, mutations will still end up in the undo stackroom.batch(() => {  room.history.disable(() => {    root.set("x", 1);    root.set("y", 2);  });});
```

#### [Async](/content/docs/api-reference/liveblocks-client\#Async/index.html)

The history API is synchronous. For long-running async tasks, call
`history.disable` at each synchronous step rather than wrapping the entire async
function:

```ts
async function generateSummary() {  room.history.disable(() => root.set("status", "generating"));  const summary = await fetchSummaryFromAgent();  room.history.disable(() => {    room.batch(() => {      root.set("summary", summary);      root.set("status", "done");    });  });}
```

Returns

- returnT

The return value of the callback.

Arguments

- fn() =\> TRequired

The callback to execute while history is disabled.

### [Room.connect](/content/docs/api-reference/liveblocks-client\#Room.connect)

Connect the local room instance to the Liveblocks server. Does nothing if the
room is already connecting, reconnecting or connected. We don’t recommend using
this API directly.

```ts
room.connect();
```

Returns

_Nothing_

Arguments

_None_

### [Room.reconnect](/content/docs/api-reference/liveblocks-client\#Room.reconnect)

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

```ts
room.reconnect();
```

Returns

_Nothing_

Arguments

_None_

### [Room.disconnect](/content/docs/api-reference/liveblocks-client\#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. We don’t
recommend using this API directly.

```ts
room.disconnect();
```

Returns

_Nothing_

Arguments

_None_

## [Comments](/content/docs/api-reference/liveblocks-client\#Comments/index.html)

### [Room.getThreads](/content/docs/api-reference/liveblocks-client\#Room.getThreads)

Returns threads, and their associated inbox notifications and subscriptions,
that are in the current room. It also returns the request date that can be used
for subsequent polling. It’s possible to filter for
[a thread’s resolved status](/content/docs/api-reference/liveblocks-client#filtering-resolved-status/index.html) and using
[custom metadata](/content/docs/api-reference/liveblocks-client#filtering-metadata/index.html).

```ts
const { threads, inboxNotifications, requestedAt } = await room.getThreads();
// [{ id: "th_s436g8...", type: "thread" }, ...]console.log(threads);
// [{ id: "in_fwh3d4...", kind: "thread", }, ...]console.log(inboxNotifications);
```

Returns

- threadsThreadData\[\]

Threads within the current room.

- inboxNotificationsInboxNotificationData\[\]

Inbox notifications associated with the threads.

- subscriptionsSubscriptionData\[\]

Subscriptions associated with the threads.

- requestedAtDate

The request date to use for subsequent polling.

Options

- resolvedboolean

Only return `resolved` or `unresolved` threads. [Learn more](/content/docs/api-reference/liveblocks-client#filtering-resolved-status/index.html).

- subscribedboolean

Only return `subscribed` or `unsubscribed` threads. [Learn more](/content/docs/api-reference/liveblocks-client#filtering-subscribed-status/index.html).

- metadataPartial<ThreadMetadata>

Only return threads containing the custom metadata. Metadata is set yourself when creating a thread, for example `{ priority: "HIGH" }`. [Learn more](/content/docs/api-reference/liveblocks-client#filtering-metadata/index.html).

#### [Filtering resolved status](/content/docs/api-reference/liveblocks-client\#filtering-resolved-status/index.html)

You can filter threads by those that are resolved, or unresolved, by passing a
`boolean` to `query.resolved`.

```ts
// Filtering for threads that are unresolvedconst threads = await room.getThreads({  query: {    resolved: false,  },});
```

#### [Filtering subscribed status](/content/docs/api-reference/liveblocks-client\#filtering-subscribed-status/index.html)

You can filter threads by those that the user is subscribed to, or not, by
passing a `boolean` to `query.subscribed`.

```ts
// Filtering for threads that the user is subscribed toconst threads = await room.getThreads({  query: {    subscribed: true,  },});
```

#### [Filtering metadata](/content/docs/api-reference/liveblocks-client\#filtering-metadata/index.html)

You can define custom metadata when
[creating a thread](/content/docs/api-reference/liveblocks-client#Room.createThread),
and the `query.metadata` option allows you to return only threads that match.

```ts
// Creating a thread with `priority` metadataawait room.createThread({  body: {    // ...  },  metadata: { priority: "HIGH" },});
// Filtering for threads with the same metadataconst threads = await room.getThreads({  query: {    metadata: { priority: "HIGH" },  },});
```

You can also filter for metadata that begins with a specific string.

```ts
// Creating a thread with `{ assigned: "sales:stacy" } metadataawait room.createThread({  body: {    // ...  },  metadata: { assigned: "sales:stacy" },});
// Filtering for threads with `assigned` metadata that starts with `sales:`const threads = await room.getThreads({  query: {    metadata: {      assigned: {        startsWith: "sales:",      },    },  },});
```

You can also filter for metadata using numeric operators.

```ts
// Creating a thread with `{ posX: 87, level: 5 } metadataawait room.createThread({  body: {    // ...  },  metadata: { posX: 87, level: 5 },});
// Filtering for threads with `posX` greater than 50 and lower than 100, and level greater than or equal to 5const threads = await room.getThreads({  query: {    metadata: {      posX: {        gt: 50,        lt: 100,      },      level: {        gte: 5,      },    },  },});
```

### [Room.getThreadsSince](/content/docs/api-reference/liveblocks-client\#Room.getThreadsSince)

Returns threads, and their associated inbox notifications and subscriptions,
that have been updated or deleted since the requested date. Helpful when used in
combination with [`Room.getThreads`](/content/docs/api-reference/liveblocks-client#Room.getThreads) to initially fetch all
threads, then receive updates later.

```ts
const initial = await room.getThreads();
const { threads, inboxNotifications, subscriptions, requestedAt } =  await room.getThreadsSince({ since: initial.requestedAt });
// { updated: [{ id: "th_s4368s...", type: "thread" }, ...], deleted: [...] }console.log(threads);
// { updated: [{ id: "in_ds83hs...", kind: "thread", }, ...], deleted: [...] }console.log(inboxNotifications);
// { updated: [{ subjectId: "th_s4368s...", kind: "thread", }, ...], deleted: [...] }console.log(subscriptions);
```

Returns

- threadsSee full type

Threads that have been updated or deleted since the requested date.

- inboxNotificationsSee full type

Inbox notifications that have been updated or deleted since the requested
date.

- subscriptionsSee full type

Subscriptions that have been updated or deleted since the requested date.

- requestedAtDate

The request date to use for subsequent polling.

Options

- sinceDateRequired

Only return threads that have been updated or deleted after this date.

### [Room.getThread](/content/docs/api-reference/liveblocks-client\#Room.getThread)

Returns a thread and its associated inbox notification and subscription, from
its ID, if it exists.

```ts
const { thread, inboxNotification, subscription } =  await room.getThread("th_xxx");
```

The thread ID can be retrieved from existing threads.

```ts
const newThread = await room.createThread(/* ... */);
const { thread, inboxNotification } = await room.getThread(newThread.id);
```

Returns

- threadThreadData \| undefined

The requested thread, or `undefined` if it doesn’t exist.

- inboxNotificationInboxNotificationThreadData \| undefined

The inbox notification associated with the thread, or `undefined` if it
doesn’t exist.

- subscriptionSubscriptionData \| undefined

The subscription associated with the thread, or `undefined` if it doesn’t
exist.

Arguments

- valuestringRequired

The ID of the thread you want to retrieve.

### [Room.createThread](/content/docs/api-reference/liveblocks-client\#Room.createThread)

Creates a thread, and its initial comment, in the current room. A comment’s body
is an array of paragraphs, each containing child nodes, learn more under
[creating thread content](/content/docs/api-reference/liveblocks-client#creating-thread-content/index.html).

```ts
const thread = await room.createThread({  body: {    version: 1,    content: [{ type: "paragraph", children: [{ text: "Hello" }] }],  },});
```

Returns

- valueThreadData

The thread that has been created.

Options

- bodyCommentBodyRequired

The content of the comment, see [creating thread\\
content](/content/docs/api-reference/liveblocks-client#creating-thread-content/index.html).

- attachmentIdsstring\[\]

The IDs of the comment’s attachments.

- commentMetadataCommentMetadata

Custom metadata to be attached to the initial comment, see [defining comment\\
metadata](/content/docs/api-reference/liveblocks-client#defining-comment-metadata/index.html).

- metadataThreadMetadata

Custom metadata to be attached to the thread, see [defining thread\\
metadata](/content/docs/api-reference/liveblocks-client#defining-thread-metadata/index.html).

#### [Creating thread content](/content/docs/api-reference/liveblocks-client\#creating-thread-content/index.html)

A comment’s body is an array of paragraphs, each containing child nodes. Here’s
an example of how to construct the following simple comment body, which can be
passed to `room.createThread`.

> Hello **world**
>
> _Second_ paragraph!

```tsx
import { CommentBody } from "@liveblocks/client";
const body: CommentBody = {  version: 1,  content: [    {      type: "paragraph",      children: [{ text: "Hello " }, { text: "world", bold: true }],    },    {      type: "paragraph",      children: [{ text: "Second", italic: true }, { text: " paragraph!" }],    },  ],};
const thread = await room.createThread({ body });
```

It’s also possible to create links and mentions.

> **@Jody Hekla** the
> **[Liveblocks](/content/site-root.html)** website is cool!

```ts
const body: CommentBody = {  version: 1,  content: [    {      type: "paragraph",      children: [        { type: "mention", id: "jody.hekla" },        { text: " the " },        { text: "Liveblocks", type: "link", url: "https://liveblocks.io" },        { text: " website is cool!" },      ],    },  ],};
```

#### [Defining thread metadata](/content/docs/api-reference/liveblocks-client\#defining-thread-metadata/index.html)

Custom metadata can be attached to each thread. `string`, `number`, and
`boolean` properties are allowed.

```ts
const metadata: Liveblocks["ThreadMetadata"] = {  color: "blue",  page: 3,  pinned: true,};
const thread = await room.createThread({ body, metadata });
```

### [Room.deleteThread](/content/docs/api-reference/liveblocks-client\#Room.deleteThread)

Deletes a thread by its ID.

```ts
await room.deleteThread("th_xxx");
```

Returns

_Nothing_

Arguments

- threadIdstringRequired

The ID of the thread to delete.

### [Room.editThreadMetadata](/content/docs/api-reference/liveblocks-client\#Room.editThreadMetadata)

Edits a thread’s custom metadata. Metadata can be a `string`, `number`, or
`boolean`. To delete an existing metadata property, set its value to `null`.

```ts
await room.editThreadMetadata({  threadId: "th_xxx",  metadata: {    color: "blue",    page: 3,    pinned: true,  },});
```

Returns

- metadataThreadMetadata

The thread metadata.

Options

- threadIdstringRequired

The ID of the thread.

- metadataPatchable<ThreadMetadata>Required

An object containing the metadata properties to update. Metadata can be a
`string`, `number`, or `boolean`. To delete an existing metadata property,
set its value to `null`.

### [Room.markThreadAsResolved](/content/docs/api-reference/liveblocks-client\#Room.markThreadAsResolved)

Marks a thread as resolved.

```ts
await room.markThreadAsResolved("th_xxx");
```

Returns

_Nothing_

Arguments

- threadIdstringRequired

The ID of the thread to resolve.

### [Room.markThreadAsUnresolved](/content/docs/api-reference/liveblocks-client\#Room.markThreadAsUnresolved)

Marks a thread as unresolved.

```ts
await room.markThreadAsUnresolved("th_xxx");
```

Returns

_Nothing_

Arguments

- threadIdstringRequired

The ID of the thread to resolve.

### [Room.subscribeToThread](/content/docs/api-reference/liveblocks-client\#Room.subscribeToThread)

Subscribes the user to a thread, meaning they will receive inbox notifications
when new comments are posted.

```ts
await room.subscribeToThread("th_xxx");
```

Returns

- valueSubscriptionData

The thread’s subscription.

Arguments

- threadIdstringRequired

The ID of the thread to subscribe to.

#### [Replacing room-level subscriptions](/content/docs/api-reference/liveblocks-client\#Replacing-room-level-subscriptions/index.html)

Subscribing will replace any existing subscription for the current thread
[set at room-level](/content/docs/api-reference/liveblocks-client#Room.updateSubscriptionSettings). This value can also be
overridden by a room-level call that is run afterwards.

```ts
// 1. Disables notifications for all threadsawait room.updateSubscriptionSettings({  threads: "none",});
// 2. Enables notifications just for this thread, "th_d75sF3..."await room.subscribeToThread("th_d75sF3...");
// 3. Disables notifications for all threads, including "th_d75sF3..."await room.updateSubscriptionSettings({  threads: "none",});
```

### [Room.unsubscribeFromThread](/content/docs/api-reference/liveblocks-client\#Room.unsubscribeFromThread)

Unsubscribes the user from a thread, meaning they will no longer receive inbox
notifications when new comments are posted.

```ts
await room.unsubscribeFromThread("th_xxx");
```

Returns

_Nothing_

Arguments

- threadIdstringRequired

The ID of the thread to unsubscribe from.

#### [Replacing room-level unsubscriptions](/content/docs/api-reference/liveblocks-client\#Replacing-room-level-unsubscriptions/index.html)

Unsubscribing will replace any existing unsubscription for the current thread
[set at room-level](/content/docs/api-reference/liveblocks-client#Room.updateSubscriptionSettings). This value can also be
overridden by a room-level call that is run afterwards.

```ts
// 1. Enable notifications for all threadsawait room.updateSubscriptionSettings({  threads: "all",});
// 2. Disables notifications just for this thread, "th_d75sF3..."await room.unsubscribeFromThread("th_d75sF3...");
// 3. Enables notifications for all threads, including "th_d75sF3..."await room.updateSubscriptionSettings({  threads: "all",});
```

### [Room.createComment](/content/docs/api-reference/liveblocks-client\#Room.createComment)

Creates a comment in a given thread.

```ts
const comment = await room.createComment({  threadId: "th_xxx",  body: {    version: 1,    content: [{ type: "paragraph", children: [{ text: "Hello" }] }],  },});
```

Returns

- valueCommentData

The comment that has been created.

Options

- threadIdstringRequired

The ID of the thread that the comment will be added to.

- bodyCommentBodyRequired

The content of the comment, see [creating comment\\
content](/content/docs/api-reference/liveblocks-client#creating-comment-content/index.html).

- attachmentIdsstring\[\]

The IDs of the comment’s attachments.

- metadataCommentMetadata

Custom metadata to be attached to the comment, see [defining comment\\
metadata](/content/docs/api-reference/liveblocks-client#defining-comment-metadata/index.html).

#### [Creating comment content](/content/docs/api-reference/liveblocks-client\#creating-comment-content/index.html)

A comment’s body is an array of paragraphs, each containing child nodes. Here’s
an example of how to construct the following simple comment body, which can be
passed to `room.createComment`.

> Hello **world**
>
> _Second_ paragraph!

```tsx
import { CommentBody } from "@liveblocks/client";
const thread = await room.createThread(/* ... */);
const body: CommentBody = {  version: 1,  content: [    {      type: "paragraph",      children: [{ text: "Hello " }, { text: "world", bold: true }],    },    {      type: "paragraph",      children: [{ text: "Second", italic: true }, { text: " paragraph!" }],    },  ],};
const comment = await room.createComment({ threadId: thread.id, body });
```

It’s also possible to create links and mentions.

> **@Jody Hekla** the
> **[Liveblocks](/content/site-root.html)** website is cool!

#### [Defining comment metadata](/content/docs/api-reference/liveblocks-client\#defining-comment-metadata/index.html)

Custom metadata can be attached to each comment. `string`, `number`, and
`boolean` properties are allowed.

```ts
const metadata: Liveblocks["CommentMetadata"] = {  priority: 2,  reviewed: true,};
const comment = await room.createComment({  threadId: "th_xxx",  body,  metadata,});
```

### [Room.editComment](/content/docs/api-reference/liveblocks-client\#Room.editComment)

Edits a comment, replacing its existing comment body and optionally updating its
attachments and metadata. Learn more about
[creating comment content](/content/docs/api-reference/liveblocks-client#creating-comment-content/index.html).

```ts
const comment = await room.editComment({  threadId: "th_xxx",  commentId: "cm_xxx",  body: {    version: 1,    content: [{ type: "paragraph", children: [{ text: "Hello" }] }],  },});
```

Returns

- valueCommentData

The comment that has been edited.

Options

- threadIdstringRequired

The ID of the thread containing the comment.

- commentIdstringRequired

The ID of the comment that’s being edited.

- bodyCommentBodyRequired

The content of the comment, see [creating comment\\
content](/content/docs/api-reference/liveblocks-client#creating-comment-content/index.html).

- attachmentIdsstring\[\]

The IDs of the comment’s attachments.

- metadataCommentMetadata

Custom metadata to be attached to the comment.

### [Room.editCommentMetadata](/content/docs/api-reference/liveblocks-client\#Room.editCommentMetadata)

Edits a comment’s custom metadata. Metadata can be a `string`, `number`, or
`boolean`. To delete an existing metadata property, set its value to `null`.

```ts
await room.editCommentMetadata({  threadId: "th_xxx",  commentId: "cm_xxx",  metadata: {    tag: "important",    priority: 2,    flagged: true,  },});
```

Returns

- metadataCommentMetadata

The comment metadata.

Options

- threadIdstringRequired

The ID of the thread containing the comment.

- commentIdstringRequired

The ID of the comment.

- metadataPatchable<CommentMetadata>Required

### [Room.deleteComment](/content/docs/api-reference/liveblocks-client\#Room.deleteComment)

Deletes a comment. If it is the last non-deleted comment, the thread also gets
deleted.

```ts
await room.deleteComment({  threadId: "th_xxx",  commentId: "cm_xxx",});
```

Returns

_Nothing_

Options

- threadIdstringRequired

The ID of the thread containing the comment.

- commentIdstringRequired

The ID of the comment that’s being edited.

### [Room.addReaction](/content/docs/api-reference/liveblocks-client\#Room.addReaction)

Adds a reaction from the current user on a comment.

```ts
const reaction = await room.addReaction({  threadId: "th_xxx",  commentId: "cm_xxx",  emoji: "👍",});
```

Returns

- valueCommentUserReaction

The reaction that has been created.

Options

- threadIdstringRequired

The ID of the thread containing the comment.

- commentIdstringRequired

The ID of the comment to add a reaction to.

- emojistringRequired

The emoji reaction to add.

### [Room.removeReaction](/content/docs/api-reference/liveblocks-client\#Room.removeReaction)

Removes a reaction from a comment.

```ts
await room.removeReaction({  threadId: "th_xxx",  commentId: "cm_xxx",  emoji: "👍",});
```

Returns

_Nothing_

Options

- threadIdstringRequired

The ID of the thread containing the comment.

- commentIdstringRequired

The ID of the comment to remove a reaction from.

- emojistringRequired

The emoji reaction to remove.

### [Room.prepareAttachment](/content/docs/api-reference/liveblocks-client\#Room.prepareAttachment)

Creates a local attachment from a file.

```ts
const attachment = room.prepareAttachment({  file: new File(["Hello, world!"], "hello.txt"),});
// { "id": "at_1e6nNX...", "name": "hello.txt", "type": "attachment", ... }console.log(attachment);
```

Returns

- valueCommentLocalAttachment

The local attachment that has been created.

Arguments

- fileFileRequired

The file to create the attachment from.

### [Room.uploadAttachment](/content/docs/api-reference/liveblocks-client\#Room.uploadAttachment)

Uploads a local attachment.

```ts
const attachment = room.prepareAttachment(file);await room.uploadAttachment(attachment);
```

Optionally, an
[`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
can be passed to cancel the upload.

```ts
const attachment = room.prepareAttachment(file);
// Cancel the upload after 5 secondsroom.uploadAttachment(attachment, { signal: AbortSignal.timeout(5000) });
```

Returns

- valueCommentAttachment

The attachment that has been uploaded.

Arguments

- attachmentCommentLocalAttachmentRequired

The file to create the attachment from.

- optionsUploadAttachmentOptions

A set of options.

Options

- signalAbortSignal

Only the inbox notifications updated or deleted after this date will be
returned.

### [Room.getAttachmentUrl](/content/docs/api-reference/liveblocks-client\#Room.getAttachmentUrl)

Returns a presigned URL for an attachment by its ID.

```ts
const url = await room.getAttachmentUrl("at_xxx");
// "https://..."console.log(url);
```

Returns

- valuestring

A presigned URL for the attachment.

Arguments

- attachmentIdstringRequired

The ID of the attachment to get the URL for.

## [Feeds](/content/docs/api-reference/liveblocks-client\#Feeds/index.html)

### [Room.fetchFeeds](/content/docs/api-reference/liveblocks-client\#Room.fetchFeeds)

Fetches feeds in the current room. Returns a paginated list of feeds with an
optional cursor for fetching more.

```ts
const { feeds, nextCursor } = await room.fetchFeeds();
// [{ feedId: "feed-1", metadata: {...}, timestamp: 1234567890 }, ...]console.log(feeds);
```

A number of options are available for filtering and pagination.

```ts
const { feeds, nextCursor } = await room.fetchFeeds({  // Optional, cursor for pagination. Use nextCursor from previous response  cursor: "abc123",
  // Optional, only return feeds created or updated after this timestamp (ms)  since: 1234567890000,
  // Optional, limit the number of feeds to return  limit: 50,
  // Optional, filter feeds by metadata. Only feeds with matching metadata are returned  metadata: {    channel: true,    name: "My Feed",  },});
```

Returns

- feedsFeed\[\]

Feeds within the current room.

- nextCursorstring \| undefined

Cursor for fetching the next page of feeds.

Options

- cursorstring

Optional cursor for pagination.

- sincenumber

Optional timestamp filter (ms). Only messages whose `createdAt` is at or
after this value are included.

- limitnumber

Optional limit for the number of feeds to return.

- metadataobject

Optional filter for feeds by metadata. Only feeds with matching metadata are
returned.

### [Room.fetchFeedMessages](/content/docs/api-reference/liveblocks-client\#Room.fetchFeedMessages)

Fetches messages for a specific feed in the current room. Returns a paginated
list of messages with an optional cursor for fetching more.

```ts
const { messages, nextCursor } = await room.fetchFeedMessages("my-feed-id");
// [{ id: "msg-1", timestamp: 1234567890, data: {...} }, ...]console.log(messages);
```

```ts
const { messages, nextCursor } = await room.fetchFeedMessages("my-feed-id", {  // Optional, cursor for pagination  cursor: "abc123",
  // Optional, only return messages created after this timestamp (ms)  since: 1234567890000,
  // Optional, limit the number of messages to return  limit: 50,});
```

Returns

- messagesFeedMessage\[\]

Messages within the feed.

- nextCursorstring \| undefined

Cursor for fetching the next page of messages.

### [Room.addFeed](/content/docs/api-reference/liveblocks-client\#Room.addFeed)

Adds a new feed to the room. Changes are synchronized in real-time to all
connected clients.

```ts
room.addFeed("my-feed-id");
// With optional metadata and timestamproom.addFeed("my-feed-id", {  metadata: { name: "My Feed", channel: true },  timestamp: Date.now(),});
```

Arguments

- feedIdstringRequired

The ID of the feed to create.

- options.metadataobject

Optional custom metadata for the feed.

- options.timestampnumber

Optional timestamp in milliseconds. Defaults to current time if not
provided.

### [Room.updateFeed](/content/docs/api-reference/liveblocks-client\#Room.updateFeed)

Updates the metadata of an existing feed. Changes are synchronized in real-time
to all connected clients.

```ts
room.updateFeed("my-feed-id", {  name: "Updated Feed Name",  updated: new Date().toISOString(),});
```

Arguments

- feedIdstringRequired

The ID of the feed to update.

- metadataobjectRequired

The new metadata for the feed.

### [Room.deleteFeed](/content/docs/api-reference/liveblocks-client\#Room.deleteFeed)

Deletes a feed from the room. Changes are synchronized in real-time to all
connected clients.

```ts
room.deleteFeed("my-feed-id");
```

Arguments

- feedIdstringRequired

The ID of the feed to delete.

### [Room.addFeedMessage](/content/docs/api-reference/liveblocks-client\#Room.addFeedMessage)

Adds a new message to a feed. Changes are synchronized in real-time to all
connected clients.

```ts
room.addFeedMessage("my-feed-id", {  role: "user",  content: "Hello, world!",});
// With optional id and timestamproom.addFeedMessage(  "my-feed-id",  { role: "user", content: "Hello!" },  {    id: "my-message-id",    timestamp: Date.now(),  });
```

Arguments

- feedIdstringRequired

The ID of the feed to add the message to.

- dataobjectRequired

The message data.

- options.idstring

Optional message ID. One will be generated if not provided.

- options.timestampnumber

Optional timestamp in milliseconds. Defaults to current time if not
provided.

### [Room.updateFeedMessage](/content/docs/api-reference/liveblocks-client\#Room.updateFeedMessage)

Updates an existing feed message. Changes are synchronized in real-time to all
connected clients.

```ts
room.updateFeedMessage("my-feed-id", "my-message-id", {  role: "user",  content: "Updated content",});
```

Arguments

- feedIdstringRequired

The ID of the feed containing the message.

- messageIdstringRequired

The ID of the message to update.

- dataobjectRequired

The new message data.

### [Room.deleteFeedMessage](/content/docs/api-reference/liveblocks-client\#Room.deleteFeedMessage)

Deletes a feed message. Changes are synchronized in real-time to all connected
clients.

```ts
room.deleteFeedMessage("my-feed-id", "my-message-id");
```

Arguments

- feedIdstringRequired

The ID of the feed containing the message.

- messageIdstringRequired

The ID of the message to delete.

## [Notifications](/content/docs/api-reference/liveblocks-client\#Notifications/index.html)

### [Client.getInboxNotifications](/content/docs/api-reference/liveblocks-client\#Client.getInboxNotifications)

Returns the current user’s inbox notifications and their associated threads and
subscriptions. It also returns the request date that can be used for subsequent
polling.

```ts
const { inboxNotifications, threads, subscriptions, requestedAt } =  await client.getInboxNotifications();
// [{ id: "in_fwh3d4...", kind: "thread", }, ...]console.log(inboxNotifications);
// [{ id: "th_s436g8...", type: "thread" }, ...]console.log(threads);
// [{ subjectId: "th_s436g8...", kind: "thread", }, ...]console.log(subscriptions);
```

Returns

- inboxNotificationsInboxNotificationData\[\]

Current user’s inbox notifications.

- threadsThreadData\[\]

Threads associated with the inbox notifications.

- subscriptionsSubscriptionData\[\]

Subscriptions associated with the inbox notifications.

- requestedAtDate

The request date to use for subsequent polling.

Options

- roomIdstring

Only return inbox notifications for the given room. [Learn\\
more](/content/docs/api-reference/liveblocks-client#filtering-inbox-notifications/index.html).

- kindstring

Only return inbox notifications for the kind. [Learn\\
more](/content/docs/api-reference/liveblocks-client#filtering-inbox-notifications/index.html).

#### [Filtering inbox notifications](/content/docs/api-reference/liveblocks-client\#filtering-inbox-notifications/index.html)

You can filter inbox notifications by those that are associated with a specific
room or kind, by passing a `string` to `query.roomId` or `query.kind`.

```ts
// Filtering for inbox notifications that are associated with a specific room or kindconst { inboxNotifications } = await client.getInboxNotifications({  query: {    roomId: "room1",    kind: "thread",  },});
```

### [Client.getInboxNotificationsSince](/content/docs/api-reference/liveblocks-client\#Client.getInboxNotificationsSince)

Returns the updated and deleted inbox notifications and their associated threads
and subscriptions since the requested date. Helpful when used in combination
with [`Client.getInboxNotifications`](/content/docs/api-reference/liveblocks-client#Client.getInboxNotifications) to
initially fetch all notifications, then receive updates later.

```ts
const initial = await client.getInboxNotifications();
const { inboxNotifications, threads, subscriptions, requestedAt } =  await client.getInboxNotificationsSince({ since: initial.requestedAt });
// { updated: [{ id: "in_ds83hs...", kind: "thread", }, ...], deleted: [...] }console.log(inboxNotifications);
// { updated: [{ id: "th_s4368s...", type: "thread" }, ...], deleted: [...] }console.log(threads);
// { updated: [{ subjectId: "th_s4368s...", kind: "thread", }, ...], deleted: [...] }console.log(subscriptions);
```

Returns

- inboxNotificationsSee full type

Inbox notifications that have been updated or deleted since the requested
date.

- threadsSee full type

Threads that have been updated or deleted since the requested date.

- requestedAtDate

The request date to use for subsequent polling.

Options

- sinceDateRequired

Only the inbox notifications updated or deleted after this date will be
returned.

### [Client.getUnreadInboxNotificationsCount](/content/docs/api-reference/liveblocks-client\#Client.getUnreadInboxNotificationsCount)

Gets the number of unread inbox notifications for the current user.

```ts
const count = await client.getUnreadInboxNotificationsCount();
```

Returns

- valuenumber

Number of unread inbox notifications.

Arguments

_None_

### [Client.markAllInboxNotificationsAsRead](/content/docs/api-reference/liveblocks-client\#Client.markAllInboxNotificationsAsRead)

Marks all inbox notifications as read, for the current user.

```ts
await client.markAllInboxNotificationsAsRead();
```

Returns

_Nothing_

Arguments

_None_

### [Client.markInboxNotificationAsRead](/content/docs/api-reference/liveblocks-client\#Client.markInboxNotificationAsRead)

Marks an inbox notification as read, for the current user.

```ts
await client.markInboxNotificationAsRead("in_xxx");
```

Returns

_Nothing_

Arguments

- inboxNotificationIdstringRequired

The ID of the inbox notification to be marked as read.

### [Client.deleteAllInboxNotifications](/content/docs/api-reference/liveblocks-client\#Client.deleteAllInboxNotifications)

Deletes an inbox notification for the current user.

```ts
await client.deleteAllInboxNotifications();
```

Returns

_Nothing_

Arguments

_None_

### [Client.deleteInboxNotification](/content/docs/api-reference/liveblocks-client\#Client.deleteInboxNotification)

Deletes an inbox notification for the current user.

```ts
await client.deleteInboxNotification("in_xxx");
```

Returns

_Nothing_

Arguments

- inboxNotificationIdstringRequired

The ID of the inbox notification to be deleted.

### [Room.getSubscriptionSettings](/content/docs/api-reference/liveblocks-client\#Room.getSubscriptionSettings)

Gets the user’s subscription settings for the current room. This notates which
[`inboxNotifications`](/content/docs/api-reference/liveblocks-client#Client.getInboxNotifications)
the current user receives in the current room.

```ts
const settings = await room.getSubscriptionSettings();
```

Returns

- settings{ threads, textMentions }

Subscription settings for Liveblocks products.

- settings.threads"all" \| "replies\_and\_mentions" \| "none"

Returns the current room’s subscription settings for threads. It can return one of three values:

- `"all"` Receive notifications for every activity in every thread.
  - `"replies_and_mentions"` Receive notifications for mentions and threads you’re participating in.
  - `"none"` No notifications are received.

- settings.textMentions"mine" \| "none"

Returns the current room’s subscription settings for text mentions. It can be one of two values:

- `"mine"` Receive notifications for mentions of you.
  - `"none"` No notifications are received.

Arguments

_None_

### [Room.updateSubscriptionSettings](/content/docs/api-reference/liveblocks-client\#Room.updateSubscriptionSettings)

Updates the user’s subscription settings for the current room. Updating this
setting will change which
[`inboxNotifications`](/content/docs/api-reference/liveblocks-client#Client.getInboxNotifications)
the current user receives in the current room.

```ts
const settings = await room.updateSubscriptionSettings({  threads: "replies_and_mentions",});
```

Returns

- settings{ threads, textMentions }

Subscription settings for Liveblocks products.

- settings.threads"all" \| "replies\_and\_mentions" \| "none"

Returns the current room’s subscription settings for threads. It can return one of three values:

- settings.textMentions"mine" \| "none"

Returns the current room’s subscription settings for text mentions. It can be one of two values:

- `"mine"` Receive notifications for mentions of you.
  - `"none"` No notifications are received.

Options

- threads"all" \| "replies\_and\_mentions" \| "none"

Sets the current room’s subscription settings for threads. It can be one of three values:

- textMentions"mine" \| "none"

Sets the current room’s subscription settings for text mentions. It can be one of two values:

- `"mine"` Receive notifications for mentions of you.
  - `"none"` No notifications are received.

#### [Replacing individual thread subscriptions](/content/docs/api-reference/liveblocks-client\#Replacing-individual-thread-subscriptions/index.html)

Subscribing will replace any
[existing thread subscriptions](/content/docs/api-reference/liveblocks-client#Room.subscribeToThread) in the current room.
This value can also be overridden by a room-level call that is run afterwards.

```ts
// 1. Enables notifications just for this thread, "th_d75sF3..."await room.subscribeToThread("th_d75sF3...");
// 2. Disables notifications for all threads, including "th_d75sF3..."await room.updateSubscriptionSettings({  threads: "none",});
```

### [Client.getNotificationSettingsBeta](/content/docs/api-reference/liveblocks-client\#Client.getNotificationSettings)

Returns the user’s notification settings in the current project, in other words
which [notification webhook events](/content/docs/platform/webhooks#NotificationEvent/index.html)
will be sent for the current user. Notification settings are project-based,
which means that this returns the current user’s settings for every room.

```ts
const settings = await client.getNotificationSettings();
// { email: { thread: true, ... }, slack: { thread: false, ... }, ... }console.log(settings);
```

A user’s initial settings are set in the dashboard, and different kinds should
be enabled there. If no kind is enabled on the current channel, `null` will be
returned. For example, with the email channel:

```ts
const settings = await client.getNotificationSettings();
// { email: null, ... }console.log(settings);
```

Returns

- settingsNotificationSettings

Current user’s notification settings.

Arguments

_None_

### [Client.updateNotificationSettingsBeta](/content/docs/api-reference/liveblocks-client\#Client.updateNotificationSettings)

Updates the current user’s notification settings, which affects which
[notification webhook events](/content/docs/platform/webhooks#NotificationEvent/index.html) will be
sent for the current user. Notification settings are project-based, which means
that this modifies the current user’s settings in every room. Each notification
`kind` must first be enabled on your project’s notification dashboard page
before settings can be used.

```ts
const settings = await client.updateNotificationSettings({  email: { thread: false },  slack: { textMention: true },});
// { email: { thread: false, ... }, slack: { textMention: true, ... }, ... }console.log(settings);
```

Returns

- settingsNotificationSettings

Current user’s notification settings.

Arguments

- settingsobjectRequired

A deep partial object containing the notification settings to
update. Custom notifications can be set too.

Examples

- settings.emailNotificationChannelSettings

The email notification settings.

- settings.slackNotificationChannelSettings

The Slack notification settings.

- settings.teamsNotificationChannelSettings

The Microsoft Teams notification settings.

- settings.webPushNotificationChannelSettings

The Web Push notification settings.

## [Storage](/content/docs/api-reference/liveblocks-client\#Storage/index.html)

Each room contains Storage, a conflict-free data store that multiple users can
edit at the same time. When users make edits simultaneously, conflicts are
resolved automatically, and each user will see the same state. Storage is ideal
for storing permanent document state, such as shapes on a canvas, notes on a
whiteboard, or cells in a spreadsheet.

### [Data structures](/content/docs/api-reference/liveblocks-client\#Data-structures/index.html)

Storage provides three different conflict-free data structures, which you can
use to build your application. All structures are permanent and persist when all
users have left the room, unlike [Presence](/content/docs/ready-made-features/presence/index.html)
which is temporary.

- [`LiveObject`](/content/docs/api-reference/liveblocks-client#LiveObject/index.html) \- 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`](/content/docs/api-reference/liveblocks-client#LiveList/index.html) \- 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`](/content/docs/api-reference/liveblocks-client#LiveMap/index.html) \- 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.

### [Typing Storage](/content/docs/api-reference/liveblocks-client\#typing-storage/index.html)

To type the Storage values you receive, make sure to set your `Storage` type.

liveblocks.config.ts

```ts
import { LiveList } from "@liveblocks/client";
declare global {  interface Liveblocks {    Storage: {      animals: LiveList<{ name: string }>;    };  }}
```

### [Nesting data structures](/content/docs/api-reference/liveblocks-client\#Nesting-data-structures/index.html)

All Storage data structures can be nested, allowing you to create complex trees
of conflict-free data.

liveblocks.config.ts

```ts
import { LiveObject, LiveList, LiveMap } from "@liveblocks/client";
type Person = LiveObject<{  name: string;  pets: LiveList<string>;}>;
declare global {  interface Liveblocks {    Storage: {      people: LiveMap<string, Person>;    };  }}
```

```ts
import { LiveObject, LiveList, LiveMap } from "@liveblocks/client";
const pets = new LiveList(["Cat", "Dog"]);const person = new LiveObject({ name: "Alicia", pets });const people = new LiveMap();people.set("alicia", person);
const { root } = await room.getStorage();root.set(people);
```

Need help troubleshooting Storage?

Get the [Liveblocks DevTools extension](/content/devtools/index.html) to develop and debug your
application as you build it.

### [Room.getStorage](/content/docs/api-reference/liveblocks-client\#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 is
always a [`LiveObject`](/content/docs/api-reference/liveblocks-client#LiveObject/index.html).

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

Returns

- storage{ root: LiveObject<TStorage> }

The room’s Storage structures. `root` is a `LiveObject`, and is the root of
your Storage. Learn more about [typing Storage](/content/docs/api-reference/liveblocks-client#typing-storage/index.html).

Arguments

_None_

## [LiveObject](/content/docs/api-reference/liveblocks-client\#LiveObject/index.html)

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
`name` and `age` fields. To add typing, read more under
[typing Storage](/content/docs/api-reference/liveblocks-client#typing-storage/index.html).

```ts
type Person = LiveObject<{  name: string;  age: number;}>;
```

Keys are strings, and values can contain other Storage structures, or
JSON-serializable data. If multiple clients update the same property
simultaneously, the last modification received by the Liveblocks servers is the
winner.

### [LiveObject.from](/content/docs/api-reference/liveblocks-client\#LiveObject.from)

Creates a new `LiveObject` from a plain JSON object, recursively converting
nested objects to `LiveObject` instances and arrays to `LiveList` instances.

```ts
const liveObject = LiveObject.from({  name: "Grace",  hobbies: ["reading", "piano"],  address: { city: "New York", zip: "10001" },});
```

This is equivalent to writing:

```ts
const liveObject = new LiveObject({  name: "Grace",  hobbies: new LiveList(["reading", "piano"]),  address: new LiveObject({ city: "New York", zip: "10001" }),});
```

Returns

- liveObjectLiveObject

A new `LiveObject` with deeply converted children.

Arguments

- objJsonObjectRequired

A plain JSON object to convert into a `LiveObject`.

### [new LiveObject](/content/docs/api-reference/liveblocks-client\#LiveObject.constructor)

Create an empty `LiveObject`

```ts
import { LiveObject } from "@liveblocks/client";
const object = new LiveObject();
```

Create a `LiveObject` with initial data.

```ts
import { LiveObject } from "@liveblocks/client";
const object = new LiveObject({ firstName: "Margaret", lastName: "Hamilton" });
```

Returns

- LiveObjectLiveObject<L>

The newly created `LiveObject`.

Arguments

- initialValueL extends LsonObjectRequired

The initial value for the `LiveObject`. Can contain JSON-serializable data
and other Liveblocks conflict-free data structures.

#### [Add a LiveObject to Storage](/content/docs/api-reference/liveblocks-client\#Add-a-LiveObject-to-Storage/index.html)

The Storage root is `LiveObject` itself, so you can use [`LiveObject.set`](/content/docs/api-reference/liveblocks-client/index.html) to
add a new property to your root. If you’ve [typed Storage](/content/docs/api-reference/liveblocks-client#typing-storage/index.html)
you’ll have type hints as you build.

```ts
import { LiveObject } from "@liveblocks/client";
const { root } = await room.getStorage();
const person = new LiveObject({ name: "Alicia" });root.set("person", person);
```

### [delete](/content/docs/api-reference/liveblocks-client\#LiveObject.delete)

Delete a property from the `LiveObject`

```ts
const object = new LiveObject({ firstName: "Ada", lastName: "Lovelace" });object.delete("lastName");
// { firstName: "Ada" }object.toJSON();
```

Returns

_Nothing_

Arguments

- keystringRequired

The key of the property you’re deleting. If the property doesn’t exist,
nothing will occur.

### [get](/content/docs/api-reference/liveblocks-client\#LiveObject.get)

Get a property from the `LiveObject`.

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

Returns

- value

The value of the property. Returns `undefined` if it doesn’t exist.

Arguments

- keystringRequired

The key of the property you’re getting.

### [set](/content/docs/api-reference/liveblocks-client\#LiveObject.set)

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

```ts
const object = new LiveObject({ firstName: "Marie" });object.set("lastName", "Curie");
// { firstName: "Ada", lastName: "Curie" }object.toJSON();
```

Returns

_Nothing_

Arguments

- keystringRequired

The key of the property you’re setting.

- valueLsonObjectRequired

The value of the property you’re setting. Can contain JSON-serializable data
and other Liveblocks conflict-free data structures.

### [update](/content/docs/api-reference/liveblocks-client\#LiveObject.update)

Adds or updates multiple properties at once. Nested changes to other Storage
types will not be applied.

```ts
const object = new LiveObject({ firstName: "Grace" });object.update({ lastName: "Hopper", job: "Computer Scientist" });
// { firstName: "Grace", lastName: "Hopper", job: "Computer Scientist" }object.toJSON();
```

Returns

_Nothing_

Arguments

- valueLsonObjectRequired

The keys and values you’re updating. Can contain JSON-serializable data and
other Liveblocks conflict-free data structures. Nested changes to other
Storage types will not be applied.

### [clone](/content/docs/api-reference/liveblocks-client\#LiveObject.clone)

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

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

Returns

- clonedStructureLiveObject

The cloned `LiveObject`.

Arguments

_None_

### [toJSON](/content/docs/api-reference/liveblocks-client\#LiveObject.toJSON)

Returns a JSON-compatible snapshot of this `LiveObject` and all its nested
children. `LiveObject` values become plain objects, `LiveList` values become
arrays, and `LiveMap` values also become plain objects (not `Map` instances).
The result is cached and only recomputed when the contents change.

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

Returns

- snapshotobject

A plain JSON-compatible object. Always serializable — `JSON.stringify()`
works out of the box.

Arguments

_None_

### [reconcile](/content/docs/api-reference/liveblocks-client\#LiveObject.reconcile)

Reconciles this `LiveObject` tree to match the given JSON object. Only mutates
keys that actually changed. Keys present on this `LiveObject` but absent from
the input will be deleted. Nested structures are recursively reconciled.

```ts
const liveObject = new LiveObject({  name: "Grace",  age: 85,  nested: new LiveObject({ x: 1, y: 2 }),});
// Updates nested.y, deletes age (absent from input)liveObject.reconcile({ name: "Grace", nested: { x: 1, y: 99 } });
```

Returns

_Nothing_

Arguments

- jsonObjJsonObjectRequired

The target state to reconcile towards. Missing keys will be deleted.

### [toImmutable](/content/docs/api-reference/liveblocks-client\#LiveObject.toImmutable)

Removed in 3.18

This method has been replaced by [`.toJSON()`](/content/docs/api-reference/liveblocks-client#LiveObject.toJSON), which
returns plain objects instead of `Map` instances for `LiveMap` values, making
the result always valid JSON. Please migrate to `.toJSON()`.

### [toObject](/content/docs/api-reference/liveblocks-client\#LiveObject.toObject)

Removed in 3.18

Use `.toJSON()` instead. It’s faster, cached, and deeply converts all nested
Live structures.

## [LiveMap](/content/docs/api-reference/liveblocks-client\#LiveMap/index.html)

The `LiveMap` class is similar to a
[JavaScript Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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. To add typing, read more under [typing Storage](/content/docs/api-reference/liveblocks-client#typing-storage/index.html).

```ts
type Shapes = LiveMap<string, LiveObject<{ name: string }>>;
```

### [new LiveMap](/content/docs/api-reference/liveblocks-client\#LiveMap.constructor)

Create an empty `LiveMap`.

```ts
const map = new LiveMap();
```

Create a `LiveMap` with initial data.

```ts
const map = new LiveMap([  ["nimesh", "developer"],  ["pierre", "designer"],]);
```

Returns

- LiveMapLiveMap<string, L>

The newly created `LiveMap`.

Arguments

- initialValue\[string, L extends LsonObject\]\[\]Required

The initial value for the `LiveMap`. An array of tuples, each containing a
key and a value. The values can contain JSON-serializable data and other
Liveblocks conflict-free data structures.

#### [Add a LiveMap to Storage](/content/docs/api-reference/liveblocks-client\#Add-a-LiveMap-to-Storage/index.html)

The Storage root is a `LiveObject`, so you can create a new `LiveMap` then use
[`LiveObject.set`](/content/docs/api-reference/liveblocks-client/index.html) to add it to your root. If you’ve
[typed Storage](/content/docs/api-reference/liveblocks-client#typing-storage/index.html) you’ll have type hints as you build.

```ts
import { LiveMap } from "@liveblocks/client";
const { root } = await room.getStorage();
const people = new LiveMap([  ["vincent", "engineer"],  ["marc", "designer"],]);root.set("people", people);
```

### [delete](/content/docs/api-reference/liveblocks-client\#LiveMap.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.

```ts
const map = new LiveMap([  ["nimesh", "developer"],  ["pierre", "designer"],]);
// truemap.delete("nimesh");
// Map { "pierre" => "designer" }map.toJSON();
```

Returns

- deletedboolean

If the element existed and was removed.

Arguments

- keystringRequired

The key of the element you’re deleting. If the element doesn’t exist,
nothing will occur.

### [entries](/content/docs/api-reference/liveblocks-client\#LiveMap.entries)

Returns a new
[Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators)
object that contains the `[key, value]` pairs for each element.

```ts
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.

Returns

- iteratorIterableIterator<\[string, L\]>

A new Iterator object for the `LiveMap`, containing the `[key, value]` pairs
for each element.

Arguments

_None_

### [forEach](/content/docs/api-reference/liveblocks-client\#LiveMap.forEach)

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

```ts
const map = new LiveMap([  ["nimesh", "developer"],  ["pierre", "designer"],]);
// "developer", "designer"map.forEach((value, key, liveMap) => console.log(value));
```

Returns

_Nothing_

Arguments

- callbackSee full typeRequired

A callback for each entry. The callback is passed the current `value`,
`key`, and the `LiveMap`. Return values are ignored.

### [get](/content/docs/api-reference/liveblocks-client\#LiveMap.get)

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

```ts
const map = new LiveMap([  ["nimesh", "developer"],  ["pierre", "designer"],]);
// "developer"map.get("nimesh");
// undefinedmap.get("alicia");
```

Returns

- valueL \| undefined

The value of the entry. Returns `undefined` if it doesn’t exist.

Arguments

- keystringRequired

The key of the entry you’re getting.

### [has](/content/docs/api-reference/liveblocks-client\#LiveMap.has)

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

```ts
const map = new LiveMap([  ["nimesh", "developer"],  ["pierre", "designer"],]);
// truemap.has("nimesh");
// falsemap.has("alicia");
```

Returns

- existsboolean

Whether the entry exists.

Arguments

- keystringRequired

The key of the entry you’re getting.

### [keys](/content/docs/api-reference/liveblocks-client\#LiveMap.keys)

Returns a new
[Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators)
object that contains the keys for each element.

```ts
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.

Returns

- iteratorIterableIterator<string>

A new Iterator object for the `LiveMap`, containing the keys of each entry.

Arguments

_None_

### [set](/content/docs/api-reference/liveblocks-client\#LiveMap.set)

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

```ts
const map = new LiveMap();map.set("vincent", "engineer");
// Map { "vincent" => "engineer" }map.toJSON();
```

Returns

_Nothing_

Arguments

- keystringRequired

The key of the entry you’re setting.

- valueLsonObjectRequired

The value of the entry you’re setting. Can contain JSON-serializable data
and other Liveblocks conflict-free data structures.

### [size](/content/docs/api-reference/liveblocks-client\#LiveMap.size)

Returns the number of elements in the `LiveMap`.

```ts
const map = new LiveMap([  ["nimesh", "developer"],  ["pierre", "designer"],]);
// 2map.size;
```

Returns

- sizenumber

The number of entries in the `LiveMap`

Arguments

- _N/A_

### [values](/content/docs/api-reference/liveblocks-client\#LiveMap.values)

Returns a new
[Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators)
object that contains the values for each element.

```ts
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.

Returns

- iteratorIterableIterator<L>

A new Iterator object for the `LiveMap`, containing the values of each
entry.

Arguments

_None_

### [clone](/content/docs/api-reference/liveblocks-client\#LiveMap.clone)

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

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

Returns

- clonedStructureLiveMap

The cloned `LiveMap`.

Arguments

_None_

### [toJSON](/content/docs/api-reference/liveblocks-client\#LiveMap.toJSON)

Returns a JSON-compatible snapshot of this `LiveMap` and all its nested
children. `LiveObject` values become plain objects, `LiveList` values become
arrays, and `LiveMap` values also become plain objects (not `Map` instances).
The result is cached and only recomputed when the contents change.

```ts
const map = new LiveMap([  ["florent", new LiveObject({ role: "engineer" })],  ["marc", new LiveObject({ role: "designer" })],]);
// { florent: { role: "engineer" }, marc: { role: "designer" } }map.toJSON();
```

Returns

- snapshotobject

A plain JSON-compatible object with string keys.

Arguments

_None_

### [toImmutable](/content/docs/api-reference/liveblocks-client\#LiveMap.toImmutable)

Removed in 3.18

This method has been replaced by [`.toJSON()`](/content/docs/api-reference/liveblocks-client#LiveMap.toJSON), which returns
plain objects instead of `Map` instances, making the result always valid JSON.
Please migrate to `.toJSON()`.

## [LiveList](/content/docs/api-reference/liveblocks-client\#LiveList/index.html)

The `LiveList` class represents an ordered collection of items that is
synchronized across clients. To add typing, read more under
[typing Storage](/content/docs/api-reference/liveblocks-client#typing-storage/index.html).

```ts
type Names = LiveList<string>;
```

Items can contain other Storage structures, or JSON-serializable data.

### [new LiveList](/content/docs/api-reference/liveblocks-client\#LiveList.constructor)

Create an empty `LiveList`.

```ts
const list = new LiveList();
```

Create a `LiveList` with initial data.

```ts
const list = new LiveList(["adrien", "jonathan"]);
```

Returns

- LiveListLiveList<L>

The newly created `LiveList`.

Arguments

- initialValueArray<L extends LsonObject>Required

The initial array of values for the `LiveList`. Can contain
JSON-serializable data and other Liveblocks conflict-free data structures.

### [clear](/content/docs/api-reference/liveblocks-client\#LiveList.clear)

Removes all the elements.

```ts
const list = new LiveList(["adrien", "jonathan"]);list.clear();
// []list.toJSON();
```

Returns

_Nothing_

Arguments

_None_

### [delete](/content/docs/api-reference/liveblocks-client\#LiveList.delete)

Deletes the element living at the specified index locally. If the index doesn't
exist, an `Error` is thrown.

```ts
const list = new LiveList(["adrien", "jonathan"]);list.delete(0);
// ["jonathan"]list.toJSON();
```

This operation uses ID-based semantics, not position-based. When called, it
reads the item at the specified index from the local state, then sends a "delete
item with ID X" instruction to the server.

If clients A and B both see a LiveList containing `["foo", "bar"]`, and client A
calls `.insert("qux", 0)`, while client B simultaneously calls `.delete(0)`, the
end result will always be `["qux", "bar"]` on both clients, and never
`["foo", "bar"]`.

Returns

_Nothing_

Arguments

- indexnumberRequired

The index of the property you’re deleting. If the property doesn’t exist, an
`Error` is thrown.

### [every](/content/docs/api-reference/liveblocks-client\#LiveList.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.

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

Returns

- isEveryboolean

Whether all elements pass the test implemented by the provided function.

Arguments

- callback(value: L, index: number) => unknownRequired

A function to execute for each item in the array. It should return a truthy
value to indicate the element passes the test, and a falsy value otherwise.
The function is passed the `value` of the item and its current `index`.

### [filter](/content/docs/api-reference/liveblocks-client\#LiveList.filter)

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

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

Returns

- filteredArrayL\[\]

An array containing each item of the `LiveList` that passed the test
implemented by the provided function.

Arguments

- callback(value: L, index: number) => unknownRequired

### [find](/content/docs/api-reference/liveblocks-client\#LiveList.find)

Returns the first element that satisfies the provided testing function. If no
item passes the test, `undefined` is returned.

```ts
const list = new LiveList(["apple", "lemon", "tomato"]);
// "lemon"list.find((value, index) => value.startsWith("l"));
```

Returns

- itemL \| undefined

The item that has been found. If no item passes the test, `undefined` is
returned.

Arguments

- callback(value: L, index: number) => unknownRequired

### [findIndex](/content/docs/api-reference/liveblocks-client\#LiveList.findIndex)

Returns the index of the first element in the `LiveList` that satisfies the
provided testing function. If no item passes the test, `-1` is returned.

```ts
const list = new LiveList(["apple", "lemon", "tomato"]);
// 1list.findIndex((value, index) => value.startsWith("l"));
```

Returns

- indexnumber

The index of the item that has been found. If no item passes the test, `-1`
is returned.

Arguments

- callback(value: L, index: number) => unknownRequired

### [forEach](/content/docs/api-reference/liveblocks-client\#LiveList.forEach)

Executes a provided function once for each element.

```ts
const list = new LiveList(["adrien", "jonathan"]);
// "adrien", "jonathan"list.forEach((item) => console.log(item));
```

Returns

_Nothing_

Arguments

- callbackSee full typeRequired

A callback for each item. The callback is passed the current `value` and
`index`. Return values are ignored.

### [get](/content/docs/api-reference/liveblocks-client\#LiveList.get)

Get the element at the specified index. Returns `undefined` if the index doesn’t
exist.

```ts
const list = new LiveList(["adrien", "jonathan"]);
// "jonathan"list.get(1);
```

Returns

- itemL \| undefined

The value of the item at the index. Returns `undefined` if it doesn’t exist.

Arguments

- indexnumberRequired

The index of the item you’re getting.

### [indexOf](/content/docs/api-reference/liveblocks-client\#LiveList.indexOf)

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

```ts
const list = new LiveList(["adrien", "jonathan"]);
// 1list.indexOf("jonathan");
// undefinedlist.indexOf("chris");
```

Returns

- indexnumber

The index of the item. Returns `-1` if it doesn’t exist.

Arguments

- searchElementLRequired

The item you’re locating.

- indexnumber

The index to start the search at.

### [insert](/content/docs/api-reference/liveblocks-client\#LiveList.insert)

Inserts one element at a specified index. Throws an `Error` if the index is out
of bounds.

```ts
const list = new LiveList(["adrien", "jonathan"]);
list.insert("chris", 1);
// ["adrien", "chris", "jonathan"]list.toJSON();
```

Returns

_Nothing_

Arguments

- valueL extends LsonObjectRequired

The value of the item you’re inserting.

- indexnumberRequired

The index to insert the item into.

### [lastIndexOf](/content/docs/api-reference/liveblocks-client\#LiveList.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. Returns `-1` if it is not present.

```ts
const list = new LiveList(["adrien", "jonathan", "adrien"]);
// 2list.indexOf("adrien");
// undefinedlist.indexOf("chris");
```

Returns

- indexnumber

The index of the item. Returns `-1` if it doesn’t exist.

Arguments

- searchElementLRequired

The item you’re locating.

- indexnumber

The index at which to start searching backwards.

### [length](/content/docs/api-reference/liveblocks-client\#LiveList.length)

Returns the number of elements.

```ts
const list = new LiveList(["adrien", "jonathan"]);
// 2list.length;
```

Returns

- lengthnumber

The number of items in the `LiveList`.

Arguments

- _N/A_

### [map](/content/docs/api-reference/liveblocks-client\#LiveList.map)

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

```ts
const list = new LiveList(["apple", "lemon", "tomato"]);
// ["APPLE", "LEMON", "TOMATO"]list.map((value, index) => value.toUpperCase());
```

Returns

- array

The array of each item has been transformed by the callback function.

Arguments

- callbackSee full typeRequired

A callback for each item. The callback is passed the current `value` and
`index`. Return values are used in the returned array.

### [move](/content/docs/api-reference/liveblocks-client\#LiveList.move)

Moves one element at a specified index.

```ts
const list = new LiveList(["adrien", "chris", "jonathan"]);
list.move(2, 0);
// ["jonathan", "adrien", "chris"]list.toJSON();
```

Returns

_Nothing_

Arguments

- indexnumberRequired

The index of the item to move.

- targetIndexnumberRequired

The index where the element should be after moving.

### [push](/content/docs/api-reference/liveblocks-client\#LiveList.push)

Adds one element to the end of the `LiveList`.

```ts
const list = new LiveList(["adrien", "jonathan"]);
list.push("chris");
// ["adrien", "jonathan", "chris"]list.toJSON();
```

Returns

_Nothing_

Arguments

- indexLRequired

The item to add to the end of the `LiveList`.

### [set](/content/docs/api-reference/liveblocks-client\#LiveList.set)

Replace one element at the specified index.

```ts
const list = new LiveList(["adrien", "jonathan"]);
list.set(1, "chris");
// equals ["adrien", "chris"]list.toJSON();
```

### [some](/content/docs/api-reference/liveblocks-client\#LiveList.some)

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

```ts
const list = new LiveList(["apple", "lemon", "tomato"]);
// truelist.some((value, index) => value.startsWith("l"));
// falselist.some((value, index) => value.startsWith("x"));
```

Returns

- areSomeboolean

Whether any elements pass the test implemented by the provided function.

Arguments

- callback(value: L, index: number) => unknownRequired

### [clone](/content/docs/api-reference/liveblocks-client\#LiveList.clone)

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

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

Returns

- clonedStructureLiveList

The cloned `LiveList`.

Arguments

_None_

### [toJSON](/content/docs/api-reference/liveblocks-client\#LiveList.toJSON)

Returns a JSON-compatible snapshot of this `LiveList` and all its nested
children. `LiveObject` values become plain objects, `LiveList` values become
arrays, and `LiveMap` values also become plain objects (not `Map` instances).
The result is cached and only recomputed when the contents change.

```ts
const list = new LiveList([  new LiveObject({ name: "Olivier" }),  new LiveObject({ name: "Vincent" }),]);
// [{ name: "Olivier" }, { name: "Vincent" }]list.toJSON();
```

Returns

- snapshotarray

A plain JSON-compatible array. Always serializable — `JSON.stringify()`
works out of the box.

Arguments

_None_

### [toImmutable](/content/docs/api-reference/liveblocks-client\#LiveList.toImmutable)

Removed in 3.18

This method has been replaced by [`.toJSON()`](/content/docs/api-reference/liveblocks-client#LiveList.toJSON), which returns
plain objects instead of `Map` instances for `LiveMap` values, making the result
always valid JSON. Please migrate to `.toJSON()`.

### [toArray](/content/docs/api-reference/liveblocks-client\#LiveList.toArray)

Deprecated

Use [`.toJSON()`](/content/docs/api-reference/liveblocks-client#LiveList.toJSON) instead. It’s faster, cached, and deeply
converts all nested Live structures.

## [Resolvers](/content/docs/api-reference/liveblocks-client\#Resolvers/index.html)

### [invalidateUsers](/content/docs/api-reference/liveblocks-client\#invalidateUsers/index.html)

`client.resolvers.invalidateUsers` can be used to invalidate some or all users
that were previously cached by [`resolveUsers`](/content/docs/api-reference/liveblocks-client#createClientResolveUsers/index.html).

It can be used when updating the current user’s avatar for example, to instantly
refresh the user data everywhere without having to perform a page reload.

```tsx
// Invalidate all usersclient.resolvers.invalidateUsers();
// Only invalidate "user-0" and "user-1"client.resolvers.invalidateUsers(["user-0", "user-1"]);
```

### [invalidateRoomsInfo](/content/docs/api-reference/liveblocks-client\#invalidateRoomsInfo/index.html)

`client.resolvers.invalidateRoomsInfo` can be used to invalidate some or all
rooms that were previously cached by
[`resolveRoomsInfo`](/content/docs/api-reference/liveblocks-client#createClientResolveRoomsInfo/index.html).

It can be used when updating a room’s name for example, to instantly refresh the
room info everywhere without having to perform a page reload.

```tsx
// Invalidate all roomsclient.resolvers.invalidateRoomsInfo();
// Only invalidate "room-0" and "room-1"client.resolvers.invalidateRoomsInfo(["room-0", "room-1"]);
```

### [invalidateGroupsInfo](/content/docs/api-reference/liveblocks-client\#invalidateGroupsInfo/index.html)

`client.resolvers.invalidateGroupsInfo` can be used to invalidate some or all
groups that were previously cached by
[`resolveGroupsInfo`](/content/docs/api-reference/liveblocks-client#createClientResolveGroupsInfo/index.html).

It can be used when updating a group’s name for example, to instantly refresh
the group info everywhere without having to perform a page reload.

```tsx
// Invalidate all groupsclient.resolvers.invalidateGroupsInfo();
// Only invalidate "group-0" and "group-1"client.resolvers.invalidateGroupsInfo(["group-0", "group-1"]);
```

### [invalidateMentionSuggestions](/content/docs/api-reference/liveblocks-client\#invalidateMentionSuggestions/index.html)

`client.resolvers.invalidateMentionSuggestions` can be used to invalidate all
mention suggestions that were previously cached by
[`resolveMentionSuggestions`](/content/docs/api-reference/liveblocks-client#createClientResolveMentionSuggestions/index.html).

It can be used when updating a room’s list of users for example, to prevent
creating out-of-date mentions without having to perform a page reload.

```tsx
// Invalidate all mention suggestionsclient.resolvers.invalidateMentionSuggestions();
```

## [Utilities](/content/docs/api-reference/liveblocks-client\#Utilities/index.html)

### [getMentionsFromCommentBody](/content/docs/api-reference/liveblocks-client\#get-mentions-from-comment-body/index.html)

Returns an array of mentions from a `CommentBody` (found under `comment.body`).

```ts
import { getMentionsFromCommentBody } from "@liveblocks/client";
const mentions = getMentionsFromCommentBody(comment.body);
```

An optional second argument can be used to filter the returned mentions. By
default, if it’s not provided, all mentions are returned, including future
mention kinds (e.g. group mentions in the future).

```tsx
// All mentions (same as `getMentionsFromCommentBody(commentBody)`)getMentionsFromCommentBody(commentBody);
// Only user mentions with an ID of "123"getMentionsFromCommentBody(  commentBody,  (mention) => mention.kind === "user" && mention.id === "123");
// Only mentions with an ID which starts with "prefix:"getMentionsFromCommentBody(commentBody, (mention) => (  mention.id.startsWith("prefix:"));
```

Here’s an example with a custom `CommentBody`.

```ts
import { CommentBody, getMentionsFromCommentBody } 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 mentions = getMentionsFromCommentBody(commentBody);
// [{ kind: "user", id: "chris@example.com" }]console.log(mentions);
```

Also available from @liveblocks/node

If you’d like to use this on the server side, it’s also available from
[`@liveblocks/node`](/content/docs/api-reference/liveblocks-node#get-mentions-from-comment-body/index.html).

### [stringifyCommentBody](/content/docs/api-reference/liveblocks-client\#stringify-comment-body/index.html)

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

```ts
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.

```ts
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` and `group` are the optional data returned from `resolveUsers` and `resolveGroupsInfo`    mention: ({ element, user, group }) =>      `<a href="${user?.profileUrl ?? group?.settingsUrl ?? "#"}">${        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 __getUsersFromDB__(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,    }));  },
  // Optional, get your group’s names and info from their ID to be displayed in mentions  async resolveGroupsInfo({ groupIds }) {    const groupsData = await __getGroupsFromDB__(groupIds);
    return groupsData.map((groupData) => ({      // Name is inserted into the output instead of a group’s ID      name: groupData.name,
      // Custom formatting in `elements.mention` allows custom properties to be used      settingsUrl: groupData.settingsUrl,    }));  },});
```

Also available from @liveblocks/node

If you’d like to use this on the server side, it’s also available from
[`@liveblocks/node`](/content/docs/api-reference/liveblocks-node#stringify-comment-body/index.html).

#### [Formatting examples](/content/docs/api-reference/liveblocks-client\#Formatting-examples/index.html)

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

```ts
// "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](/content/site-root.html)"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" }];  },});
```

## [TypeScript](/content/docs/api-reference/liveblocks-client\#TypeScript/index.html)

### [Typing your data](/content/docs/api-reference/liveblocks-client\#Typing-your-data/index.html)

It’s possible to have automatic types flow through your application by defining
a global `Liveblocks` interface. We recommend doing this in a
`liveblocks.config.ts` file in the root of your app, so it’s easy to keep track
of your types. Each type (`Presence`, `Storage`, etc.), is optional, but it’s
recommended to make use of them.

liveblocks.config.ts

```ts
declare global {  interface Liveblocks {    // Each user’s Presence    Presence: {};
    // The Storage tree for the room    Storage: {};
    UserMeta: {      id: string;      // Custom user info set when authenticating with a secret key      info: {};    };
    // Custom events    RoomEvent: {};
    // Custom metadata set on threads    ThreadMetadata: {};
    // Custom metadata set on comments    CommentMetadata: {};
    // Custom room info set with resolveRoomsInfo    RoomInfo: {};
    // Custom group info set with resolveGroupsInfo    GroupInfo: {};
    // Custom activities data for custom notification kinds    ActivitiesData: {};  }}
// Necessary if you have no imports/exportsexport {};
```

Here are some example values that might be used.

liveblocks.config.ts

```ts
import { LiveList } from "@liveblocks/client";
declare global {  interface Liveblocks {    // Each user’s Presence    Presence: {      // Example, real-time cursor coordinates      cursor: { x: number; y: number };    };
    // The Storage tree for the room    Storage: {      // Example, a conflict-free list      animals: LiveList<string>;    };
    UserMeta: {      id: string;      // Custom user info set when authenticating with a secret key      info: {        // Example properties        name: string;        avatar: string;      };    };
    // Custom events    // Example has two events, using a union    RoomEvent: { type: "PLAY" } | { type: "REACTION"; emoji: "🔥" };
    // Custom metadata set on threads    ThreadMetadata: {      // Example, attaching coordinates to a thread      x: number;      y: number;    };
    // Custom metadata set on comments    CommentMetadata: {      // Example, attaching a tag and a spam flag to a comment      tag: string;      spam: boolean;    };
    // Custom room info set with resolveRoomsInfo    RoomInfo: {      // Example, rooms with a title and url      title: string;      url: string;    };
    // Custom group info set with resolveGroupsInfo    GroupInfo: {      // Example, groups with a name and a badge      name: string;      badge: string;    };
    // Custom activities data for custom notification kinds    ActivitiesData: {      // Example, a custom $alert kind      $alert: {        title: string;        message: string;      };    };  }}
// Necessary if you have no imports/exportsexport {};
```

### [Typing with client.enter](/content/docs/api-reference/liveblocks-client\#Typing-with-client.enter)

Before Liveblocks 2.0, it was recommended to type your data by passing
`Presence`, `Storage`, `UserMeta`, and `RoomEvents` types to
[`client.enterRoom`](/content/docs/api-reference/liveblocks-client#Client.enterRoom). This is no longer
[the recommended method](/content/docs/api-reference/liveblocks-client#Typing-your-data/index.html) for setting up Liveblocks, but it
can still be helpful, for example you can use `client.enter` multiple times to
create different room types, each with their own correctly typed hooks.

```ts
import { LiveList } from "@liveblocks/client";
// Each user’s Presencetype Presence = {  cursor: { x: number; y: number };};
// The Storage tree for the roomtype Storage = {  animals: LiveList<string>;};
// User information set when authenticating with a secret keytype UserMeta = {  id: string;  info: {    // Custom properties, corresponds with userInfo  };};
// Custom events that can be broadcast, use a union for multiple eventstype RoomEvent = {  type: "REACTION";  emoji: "🔥";};
const { room, leave } = client.enterRoom<  Presence,  Storage,  UserMeta,  RoomEvent>("my-room-id");
```

You can also pass types to
[`client.getRoom`](/content/docs/api-reference/liveblocks-client#Client.getRoom).

```ts
const { room, leave } = client.getRoom<Presence, Storage, UserMeta, RoomEvent>(  "my-room-id");
```

### [ToolDefinition](/content/docs/api-reference/liveblocks-client\#ToolDefinition/index.html)

Type definition for AI tools that can be executed by the AI. This type
represents the structure of a tool that can be registered and used in AI
interactions.

```ts
type ToolDefinition<TArgs = any, TResult = any> = {  description: string;  parameters: JSONSchema7;  execute: (    args: TArgs,    context: ToolExecutionContext  ) => Promise<{ data: TResult }>;  render: (props: ToolRenderProps<TArgs, TResult>) => ReactNode;};
```

Properties

- descriptionstring

A clear description of what the tool does. Used by AI to understand when to
call this tool.

- parametersJSONSchema7

JSON Schema defining the tool’s input parameters. The AI will validate
arguments against this schema.

- executefunction

Async function that performs the tool’s action. Receives validated arguments
and execution context, returns structured data.

- renderfunction

React component function that renders the tool’s UI during different
execution stages.

### [AiKnowledgeSource](/content/docs/api-reference/liveblocks-client\#AiKnowledgeSource/index.html)

Type definition for knowledge sources that provide contextual information to AI.
Knowledge sources help the AI understand your application’s current state and
make more informed responses.

```ts
type AiKnowledgeSource = {  description: string;  value: string;};
```

Properties

- descriptionstring

A clear description of what this knowledge represents (e.g., "Current user’s
profile", "Application settings").

- valuestring \| object \| array \| number \| boolean \| null

The knowledge content. Can be any JSON-compatible format that provides
context to the AI.

Knowledge sources can be registered using
[`RegisterAiKnowledge`](/content/docs/api-reference/liveblocks-react#RegisterAiKnowledge/index.html)
or passed directly to [`AiChat`](/content/docs/api-reference/liveblocks-react-ui#AiChat/index.html)
via the `knowledge` prop.

```ts
// Example knowledge sourcesconst userKnowledge: AiKnowledgeSource = {  description: "Current user information",  value: { name: "John Doe", role: "admin" },};
const appStateKnowledge: AiKnowledgeSource = {  description: "Current application state",  value: "The user is currently editing a document in dark mode",};
```

### [User](/content/docs/api-reference/liveblocks-client\#user-type/index.html)

`User` is a type that’s returned by [`room.getSelf`](/content/docs/api-reference/liveblocks-client#Room.getSelf), [`room.getOthers`](/content/docs/api-reference/liveblocks-client#Room.getOthers),
and other functions. Some of its values are set when
[typing your room](/content/docs/api-reference/liveblocks-client#Typing-your-data/index.html), here are some example values:

liveblocks.config.ts

```ts
declare global {  interface Liveblocks {    // Each user’s Presence    Presence: {      cursor: { x: number; y: number };    };
    UserMeta: {      id: string;      // Custom user info set when authenticating with a secret key      info: {        name: string;        avatar: string;      };    };  }}
```

```ts
const { room, leave } = client.enterRoom("my-room-id");
// {//   connectionId: 52,//   presence: {//     cursor: { x: 263, y: 786 },//   },//   id: "mislav.abha@example.com",//   info: {//     name: "Mislav Abha",//     avatar: "/mislav.png",//   },//   canWrite: true,//   canComment: true,// }const user = room.getSelf();
```

Properties

- connectionIdnumber

The connection ID of the User. It is unique and increments with every new
connection.

- idUserMeta\["id"\]

The ID of the User that has been set in the authentication endpoint. Useful
to get additional information about the connected user.

- infoUserMeta\["info"\]

Additional user information that has been set in the authentication
endpoint.

- presenceTPresence

The user’s Presence data.

- canWriteboolean

`true` if the user can mutate the Room’s Storage and/or YDoc, `false` if
they can only read but not mutate it. Set via your [room\\
permissions](/content/docs/authentication#Room-permissions/index.html).

- canCommentboolean

`true` if the user can leave a comment in the room, `false` if they can only
read comments but not leave them. Set via your [room\\
permissions](/content/docs/authentication#Room-permissions/index.html).
