Liveblocks

Chat SDK adapter backed by Liveblocks Comments. Maps Liveblocks rooms, threads, and comments to the Chat SDK Channel/Thread/Message model.

Vendor-official adapter maintained by Liveblocks, not Vercel or Chat SDK contributors. For feature requests, bug reports, and support, file an issue on the adapter's repo.

Install

pnpm add @liveblocks/chat-sdk-adapter

Quick start

lib/bot.ts
import { Chat } from "chat";
import { createMemoryState } from "@chat-adapter/state-memory";
import {
  createLiveblocksAdapter,
  type LiveblocksAdapter,
} from "@liveblocks/chat-sdk-adapter";

const bot = new Chat<{ liveblocks: LiveblocksAdapter }>({
  userName: "MyBot",
  adapters: {
    liveblocks: createLiveblocksAdapter({
      apiKey: process.env.LIVEBLOCKS_SECRET_KEY,
      webhookSecret: process.env.LIVEBLOCKS_WEBHOOK_SECRET,
      botUserId: "my-bot-user",
      botUserName: "MyBot",
    }),
  },
  state: createMemoryState(),
});

bot.onNewMention(async (thread, message) => {
  await thread.adapter.addReaction(thread.id, message.id, "👀");
  await thread.post(`Hello, ${message.author.userName}!`);
});

The adapter maps Liveblocks rooms to Chat SDK channels, comment threads to Chat SDK threads, and individual comments to messages — so the rest of the Chat SDK API (subscriptions, handlers, posts, reactions) works exactly the same as with any other adapter.

Configuration

Prop

Type

Resolver return types follow the @liveblocks/core user and group metadata shapes (U["info"], DGI).

Resolving mentions

When comments contain @mentions, supply resolveUsers (and optionally resolveGroupsInfo):

lib/bot.ts
const adapter = createLiveblocksAdapter({
  apiKey: process.env.LIVEBLOCKS_SECRET_KEY,
  webhookSecret: process.env.LIVEBLOCKS_WEBHOOK_SECRET,
  botUserId: "my-bot-user",

  resolveUsers: async ({ userIds }) => {
    const users = await getUsersFromDatabase(userIds);
    return users.map((user) => ({
      name: user.fullName,
      avatar: user.avatarUrl,
    }));
  },

  resolveGroupsInfo: async ({ groupIds }) => {
    const groups = await getGroupsFromDatabase(groupIds);
    return groups.map((group) => ({ name: group.displayName }));
  },
});

Platform setup

  1. Create a Liveblocks project with rooms using Comments.
  2. In the dashboard, copy a secret key (sk_...) for server-side REST API calls.
  3. Create a webhook signing secret (whsec_...) and configure webhooks to subscribe to:
    • commentCreated
    • commentReactionAdded
    • commentReactionRemoved
  4. Choose a stable botUserId consistent with how your app identifies users — either a real user ID in your system or a dedicated bot ID you issue.

Point your Liveblocks webhook URL at the route that forwards requests to bot.webhooks.liveblocks (see Webhook events).

Webhook events

Supported Liveblocks webhook types:

EventRole
commentCreatedDrives Chat SDK message processing
commentReactionAddedDrives reaction handlers
commentReactionRemovedDrives reaction handlers
app/api/webhooks/liveblocks/route.ts
import { after } from "next/server";
import { bot } from "@/lib/bot";

export async function POST(request: Request) {
  return bot.webhooks.liveblocks(request, {
    waitUntil: (task) => after(() => task),
  });
}

The adapter verifies signatures with webhookSecret; invalid requests return 401. Passing waitUntil lets work continue after the response is sent in serverless environments like Vercel.

ID encoding

  • Thread ID: liveblocks:{roomId}:{threadId}
  • Channel ID: liveblocks:{roomId}

encodeThreadId

adapter.encodeThreadId(data: { roomId: string; threadId: string }): string;
const encoded = adapter.encodeThreadId({
  roomId: "my-room",
  threadId: "th_abc123",
});
// "liveblocks:my-room:th_abc123"

decodeThreadId

adapter.decodeThreadId(threadId: string): { roomId: string; threadId: string };

Throws if the format is invalid. Room IDs may contain : — the last : separates the threadId, so Liveblocks thread IDs themselves must not contain :.

Message format

Liveblocks Comments use a simpler body model than full Markdown. Outbound content from the Chat SDK is converted automatically and some structure is flattened.

Supported: paragraphs with bold, italic, code, strikethrough, links, and @mentions (users and groups).

Flattened to plain text / paragraphs: headings, bullet and numbered lists, code blocks, tables (rendered as ASCII inside a paragraph), raw HTML. Card payloads become markdown/plain text (or fallbackText); interactivity is not preserved.

Reactions

Use Unicode emoji directly, or names like thumbs_up which normalize to emoji where supported. Unknown custom IDs can fail API validation:

await thread.adapter.addReaction(thread.id, message.id, "👍");
await thread.adapter.addReaction(thread.id, message.id, "thumbs_up");

Examples

Feature support

Messaging

FeatureSupported
Post message
Edit message
Delete message
File uploadsAttachments
Streaming
Scheduled messages

Rich content

FeatureSupported
Card formatFlattened to text
Buttons
Link buttons
Select menus
TablesFlattened to text
Fields
Images in cards
Modals

Conversations

FeatureSupported
Slash commands
MentionsUsers + groups
Add reactionsUnicode emoji
Remove reactionsUnicode emoji
Typing indicator
DMs
Ephemeral messages
User lookupresolveUsers
Parent subject
Native client
Custom API endpoint

Message history

FeatureSupported
Fetch messages
Fetch single message
Fetch thread info
Fetch channel messages
List threads
Fetch channel info
Post channel message

On this page