Build a Chat Panel
Last updated Oct 19, 2026 Prerequisite
This guide builds on top of the Build Your Own UI. It is recommended to read that guide first. Portions of the code will not be repeated here for brevity.
The chat components on this page require RealtimeKit Web UI Kit 1.1.0 or later and work best with RealtimeKit Web Core 1.2.4 or later.
The UI Kit includes a full chat panel and lower-level chat components. The chat components load messages in pages, instead of loading the entire chat history when they join. This keeps chat responsive in meetings with many participants or long chat history.
Use the full panel when you want RealtimeKit to manage chat behavior for you.
Use lower-level components when you need to control the layout around message lists, pinned messages, or the composer.
This page is not available for the Mobileplatform.
WebMobile
ReactWeb ComponentsAngular
Render the full chat panel
Use rtk-chat to render the full chat panel. It includes public chat, private chat, paginated messages, pinned messages, file uploads, image uploads, edit actions, delete actions, and emoji input.
<rtk-ui-provider id="rtk-ui-provider">
<rtk-chat style="height: 480px;"></rtk-chat>
</rtk-ui-provider>
To set chat UI overrides, assign the overrides property on rtk-ui-provider or rtk-meeting.
const provider = document.querySelector("rtk-ui-provider");
provider.overrides = {
disableEmojiPicker: true,
disablePrivateChat: true,
};
The disableEmojiPicker override hides the emoji picker button. The disablePrivateChat override hides private chat even when the preset allows it.
To control private chat for all participants, prefer preset permissions.
Use RtkChat to render the full chat panel. It includes public chat, private chat, paginated messages, pinned messages, file uploads, image uploads, edit actions, delete actions, and emoji input.
import { RtkChat, RtkUiProvider } from "@cloudflare/realtimekit-react-ui";
function ChatPanel({ meeting }) {
return (
<RtkUiProvider
meeting={meeting}
overrides={{
disableEmojiPicker: true,
disablePrivateChat: true,
}}
>
<RtkChat style={{ height: "480px" }} />
</RtkUiProvider>
);
}
The disableEmojiPicker override hides the emoji picker button. The disablePrivateChat override hides private chat even when the preset allows it.
To control private chat for all participants, prefer preset permissions.
Use rtk-chat to render the full chat panel. It includes public chat, private chat, paginated messages, pinned messages, file uploads, image uploads, edit actions, delete actions, and emoji input.
<rtk-ui-provider [meeting]="meeting" [overrides]="chatOverrides">
<rtk-chat style="height: 480px;"></rtk-chat>
</rtk-ui-provider>
chatOverrides = {
disableEmojiPicker: true,
disablePrivateChat: true,
};
The disableEmojiPicker override hides the emoji picker button. The disablePrivateChat override hides private chat even when the preset allows it.
To control private chat for all participants, prefer preset permissions.
Build a custom chat layout
You can create your own chat layout from the same UI Kit components that RtkChat uses. Place each component wherever your design needs it, such as the message list in a sidebar and the composer in a footer, and connect them through your own state.
| Component | What it renders |
|---|---|
RtkChatSelector |
A dropdown to switch between public chat and private conversations. Emits rtkChatSelectorChange with the selected participant. |
RtkPinnedMessageSelector |
A collapsible header that expands to show pinned messages. |
RtkChatMessagesUiPaginated |
The message list. Loads public messages when privateChatRecipient is empty, and the private conversation when you pass a participant. |
RtkChatComposerView |
The message input, with file, image, and emoji buttons. |
When you build your own layout, your code connects the components to meeting.chat:
RtkChatMessagesUiPaginatedemitspinMessage,editMessage, anddeleteMessageevents from its message menu but does not act on them. Handle these events and call the matchingmeeting.chatmethods.RtkChatComposerViewemitsnewMessageandeditMessageevents. Send or edit the message withmeeting.chat.- Pass
meeting.chat.maxTextLimitandmeeting.chat.rateLimitstoRtkChatComposerViewso the composer enforces the same limits as the meeting.
The following example stacks the components vertically. Rearrange them to fit your layout.
import {
RtkChatComposerView,
RtkChatMessagesUiPaginated,
RtkChatSelector,
RtkPinnedMessageSelector,
} from "@cloudflare/realtimekit-react-ui";
import { useState } from "react";
function CustomChat({ meeting }) {
const [privateChatRecipient, setPrivateChatRecipient] = useState(null);
const [editingMessage, setEditingMessage] = useState(null);
const { chatPublic, chatPrivate } = meeting.self.permissions;
const publicChat = {
canSendText: chatPublic.canSend && chatPublic.text,
canSendFiles: chatPublic.canSend && chatPublic.files,
};
const privateChat = {
canSendText: Boolean(chatPrivate?.canSend && chatPrivate?.text),
canSendFiles: Boolean(chatPrivate?.canSend && chatPrivate?.files),
};
const activeChat = privateChatRecipient ? privateChat : publicChat;
return (
<div style={{ height: "480px" }}>
<RtkChatSelector
meeting={meeting}
onRtkChatSelectorChange={(event) => {
setPrivateChatRecipient(event.detail.selectedUser ?? null);
}}
/>
<RtkPinnedMessageSelector meeting={meeting} />
<RtkChatMessagesUiPaginated
meeting={meeting}
privateChatRecipient={privateChatRecipient}
onPinMessage={(event) => {
const message = event.detail;
if (message.pinned) {
meeting.chat.unpin(message.id);
} else {
meeting.chat.pin(message.id);
}
}}
onEditMessage={(event) => {
if (event.detail.type === "text") {
setEditingMessage(event.detail);
}
}}
onDeleteMessage={(event) => {
if (editingMessage?.id === event.detail.id) {
setEditingMessage(null);
}
meeting.chat.deleteMessage(event.detail.id);
}}
/>
<RtkChatComposerView
canSendTextMessage={activeChat.canSendText}
canSendFiles={activeChat.canSendFiles}
maxLength={meeting.chat.maxTextLimit}
rateLimits={meeting.chat.rateLimits}
inputTextPlaceholder="Enter a message"
isEditing={!!editingMessage}
message={editingMessage ? editingMessage.message : ""}
storageKey={
editingMessage
? `custom-chat-edit-${editingMessage.id}`
: "custom-chat-draft"
}
onNewMessage={(event) =>
meeting.chat.sendMessage(
event.detail,
privateChatRecipient ? [privateChatRecipient.id] : [],
)
}
onEditMessage={async (event) => {
try {
await meeting.chat.editTextMessage(editingMessage.id, event.detail);
} finally {
setEditingMessage(null);
}
}}
onEditCancel={() => setEditingMessage(null)}
/>
</div>
);
}
Refer to the RtkChat, RtkChatSelector, RtkPinnedMessageSelector, RtkChatMessagesUiPaginated, and RtkChatComposerView API references for component props.
Was this helpful?
YesNo
On this page
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/realtimekit/humans/ui-kit/chat-panel/#page","headline":"Build a Chat Panel","description":"Build a scalable chat panel for your custom RealtimeKit meeting UI with paginated UI Kit chat components.","url":"https://developers.cloudflare.com/realtime/realtimekit/humans/ui-kit/chat-panel/","inLanguage":"en","image":"https://developers.cloudflare.com/realtime/realtimekit/humans/ui-kit/chat-panel/og.png?v=ea17480b73fe9a5a","dateModified":"2026-10-19","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
description: Browse prebuilt RealtimeKit UI Kit components for building custom meeting interfaces. title: Component Library image: https://developers.cloudflare.com/realtime/realtimekit/humans/ui-kit/component-library/og.png?v=a74842d247ec418b
Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt
Use this file to discover all available pages before exploring further.
Docs