A thread is a set of replies to one message. Threads keep side conversations from crowding the main chat. For example, in a team channel, a question gets its answers in a thread instead of between unrelated messages.
Where this fits: your app already sends messages. A
reply is just a normal message with replyTo set to the message it answers.
There's nothing to create or set up first.
Reply and load a thread
In your app:
await chat.sendMessage({ text: 'This is a reply', replyTo: 'msg_3xR…' });
const thread = await chat.messages.thread('msg_3xR…'); // [root, reply, reply, ...] oldest firstconst { send } = useMessages();
await send('This is a reply', { replyTo: 'msg_3xR…' });There's no hook for loading a whole thread yet. Call
useChatClient()?.messages.thread(id).
await chat.send('This is a reply', replyTo: messageId);
final thread = await chat.thread(messageId); // oldest firstReact Native uses the same chat.sendMessage() and chat.messages.thread()
as web.
Thread replies are normal messages. They arrive as message events,
and search, retention, moderation, and webhooks all work on them too.
Check threadRootId on each message to decide where to show it: null
means the main chat, anything else means "inside that thread".
Threads stay flat
A reply to a reply joins the same thread. Threads don't nest:
msg_A threadRootId: null
├── msg_B replyTo: A threadRootId: A
└── msg_C replyTo: B threadRootId: A ← not B
replyTois the message you answered.threadRootIdis the thread's first message.- This keeps threads easy to follow and fast to load.
chat.messages.thread()works with any message in the thread, not only the first one.
Long threads: paging
chat.messages.thread() returns the first page of a thread. For long
threads, page through with threadPage() (at most 100 per page, oldest
first):
let page = await chat.messages.threadPage('msg_3xR…', { limit: 50 });
render(page.data);
while (page.hasMore && page.nextCursor) {
page = await chat.messages.threadPage('msg_3xR…', { cursor: page.nextCursor });
render(page.data);
}var page = await chat.threadPage(messageId, limit: 50);
while (page.hasMore) {
page = await chat.threadPage(messageId, cursor: page.nextCursor);
}In React, <Thread> pages for you. See UI components.
Reply counts
Root messages carry replyCount and lastReplyAt, so the main list can
show "4 replies · last 2 min ago" without loading the thread:
if (message.replyCount) showThreadLink(message.id, message.replyCount, message.lastReplyAt);Filtering history by thread
You can also use the threadRootId filter in
Message History. chat.messages.thread() is a
shortcut for that same filter.
Common mistakes
| Mistake | What you'll see | Fix |
|---|---|---|
Showing every message event in the main list | Thread replies mixed into the main chat. | Show messages with a threadRootId in the thread panel instead. |
| Expecting nested replies | A reply to a reply lands in the same thread. | This is by design. Use replyTo to show "replying to …" inside the thread. |