RealTimeX
Sign inStart for free
RealTimeXWeb Package
IntroductionPrerequisitesInstallationProject setupQuick start
Package structureConfigurationComponentsHooksAPI integrationSocket events
OverviewReplace entire UIReplace componentsStyle with classNamesThemes & localePhone & video callingIn-call control barFeatures
Unread badgeRich-text messagesPins, stars & datesMessage threadsMentionsCanvas & draftsNotificationsAdmin permissionsServer refusalsSession & token refresh
Host app integrationTroubleshootingFAQVersion historyBest practicesSupport & contact
Web packagePackage reference

Components

Every slot you can replace, how defaults fill in, and every classNames key.

Default component fallback

Customization is opt-in per slot. You never register every component - pass only the ones you want to replace through components on ChatMain (or uiConfig.components on Chat).

shell
1ChatMain components={{ MembersDrawer: MyDrawer }}2        ↓3ChatCustomizationProvider merges your config4        ↓5At render: resolveComponent("MembersDrawer", PackageMembersDrawer)6        ↓7Custom passed?  → use MyDrawer8Not passed?     → use PackageMembersDrawer (built-in default)
You passResult
No components prop at allFull default chat UI
components={{ Avatar: MyAvatar }}Custom avatar only; every other slot uses its default
components={{ MembersDrawer: undefined }}Treated as not passed → default drawer
classNames={{ sidebar: "..." }}Your classes merge on top of the package defaults

Your component receives the same props as the package default (MembersDrawerSlotProps, etc.) - import the prop type from the package for TypeScript.

Which slots are wired

These slots are honored at runtime when you pass them:

SlotWired inFallback when omitted
ChatLayoutChatLayout.tsxDefaultChatLayout
LoggedUserDetailsDefaultChatLayoutPackage default
ChannelListDefaultChatLayoutPackage default
ChannelHeaderDefaultChatLayoutPackage default
MessageListDefaultChatLayoutPackage default
MessageInputDefaultChatLayoutPackage default
ForwardModalDefaultChatLayoutPackage default
ChatPanelAlertsDefaultChatLayoutPackage default
ChannelListItemChannelListItem wrapperDefaultChannelListItem
AvatarChatAvatarDefaultChatAvatar
HeaderCallActionsHeaderCallActionsBuilt-in phone/video group
PhoneCallButton / VideoCallButtonHeaderCallActionsBuilt-in buttons
HeaderLeftSide / HeaderRightSideChannelHeaderViewPackage default
ChatActionModalChannelHeaderViewPackage default
ChannelDetailsDrawerChannelHeaderViewPackage default
MembersDrawer / AdminsDrawer / PermissionDrawerChannelHeaderViewPackage default
AttachmentsDrawerChannelHeaderViewPackage default
PinnedMessagesDrawer / StarredMessagesDrawerChannelHeaderViewPackage default

Tip

Replace the entire header: pass components.ChannelHeader (use the useChannelHeader hook for state and handlers). That replaces the header and every drawer inside it.

Advanced slots

Typed and exported, but used inside inner components - customize them by replacing the parent slot:

SlotCustomize by replacing
MessageItemMessageList (your list renders custom items)
MessageToolbar, MessageStatus, MessageAttachments, MessageReactions, MessageReplySnippetMessageItem or MessageList
HeaderSearchOverlayHeaderRightSide or ChannelHeader
ChatThemeToggle, ChatDateTimeSettings, ChatSocketStatusTheir header/sidebar parent, or ChannelHeader

All component slots

SlotReplacesProp type
ChatLayoutEntire chat shellChatLayoutSlotProps
LoggedUserDetailsSidebar profile + new chatLoggedUserDetailsSlotProps
GlobalSearchPopoverSidebar global searchGlobalSearchPopoverSlotProps
LoggedUserSettingsTriggerAccount settings buttonLoggedUserSettingsTriggerSlotProps
LoggedUserSettingsDialogAccount settings dialogLoggedUserSettingsDialogSlotProps
LoggedUserSettingsContentAccount settings bodyLoggedUserSettingsContentSlotProps
PushNotificationTogglePush on/off row in settingsPushNotificationToggleProps
ChannelListConversation listChannelListSlotProps
ChannelListItemOne conversation rowChannelListItemSlotProps
ChannelHeaderFull header barChannelHeaderSlotProps
HeaderLeftSideBack, name, pin/starHeaderLeftSideSlotProps
HeaderRightSideSearch, menu, settingsHeaderRightSideSlotProps
HeaderCallActionsPhone + video groupHeaderCallActionsSlotProps
PhoneCallButtonVoice call buttonPhoneCallButtonSlotProps
VideoCallButtonVideo call buttonVideoCallButtonSlotProps
HeaderSearchOverlayMessage search dropdownHeaderSearchOverlaySlotProps
MessageListScrollable messages areaActiveChannelMessagesSlotProps
MessageItemSingle message bubbleMessageItemSlotProps
MessageInputComposerChannelMessageBoxSlotProps
ForwardModalForward dialogForwardModalSlotProps
ChatPanelAlertsError/info bannerChatPanelAlertsSlotProps
ChatActionModalClear/delete/block confirmChatActionModalSlotProps
ChannelDetailsDrawerChannel details sheetChannelDetailsDrawerSlotProps
MembersDrawerGroup members sheetMembersDrawerSlotProps
AdminsDrawerGroup admins sheetAdminsDrawerSlotProps
PermissionDrawerPermissions sheetPermissionDrawerSlotProps
AttachmentsDrawerAttachments sheetAttachmentsDrawerSlotProps
PinnedMessagesDrawerPinned messages sheetPinnedMessagesDrawerSlotProps
StarredMessagesDrawerStarred messages sheetStarredMessagesDrawerSlotProps
RecordingsDrawerCall recordings sheetRecordingsDrawerSlotProps
CallsDrawerCall history / live callsCallsDrawerSlotProps
ThreadDrawerThread side panelThreadDrawerSlotProps
ThreadsInboxDialogThreads inbox dialogThreadsInboxDialogSlotProps
CanvasListPanelCanvas list in the headerCanvasListPanelProps
AvatarEvery avatar (in-call portraits keep the built-in look - style them with classNames.avatar)AvatarSlotProps
MessageToolbarReply/forward/edit menuMessageToolbarSlotProps
MessageStatusSent/delivered/read ticksMessageStatusSlotProps
MessageAttachmentsFiles/images in a bubbleMessageAttachmentsSlotProps
MessageReactionsEmoji chipsMessageReactionsSlotProps
MessageReplySnippetQuoted reply in a bubbleMessageReplySnippetSlotProps
ChatThemeToggleLight/dark toggle{ className?: string }
ChatDateTimeSettingsDate/time settings buttonRecord<string, never>
ChatSocketStatusConnection indicator{ className?: string }
CallOverlayIn-call UI (() => null hides it)Record<string, never>
IncomingCallDialogRinging dialog (() => null hides it)Record<string, never>

Code examples for common slots

MembersDrawer - use the package store for the same data and actions as the default drawer:

tsx
1import { useChatStore, type MembersDrawerSlotProps } from "@realtimexsco/live-chat";2 3const MyMembersDrawer = ({ isOpen, onOpenChange, conversationInfo }: MembersDrawerSlotProps) => {4  const manageGroupMembers = useChatStore((s) => s.manageGroupMembers);5  const participants = conversationInfo?.participants ?? [];6 7  if (!isOpen) return null;8  return (9    <aside className="members-drawer">10      <header>11        <h2>Members ({participants.length})</h2>12        <button type="button" onClick={() => onOpenChange(false)}>Close</button>13      </header>14      <ul>15        {participants.map((p, i) => {16          const id = typeof p === "string" ? p : p._id;17          const name = typeof p === "string" ? "User" : p.name;18          return <li key={`${id}-${i}`}>{name}</li>;19        })}20      </ul>21    </aside>22  );23};24// components={{ MembersDrawer: MyMembersDrawer }} - remove it to get the default back.

ChannelDetailsDrawer

tsx
1import type { ChannelDetailsDrawerSlotProps } from "@realtimexsco/live-chat";2 3const MyDetailsDrawer = ({ isOpen, onOpenChange, activeChannel, conversationInfo }: ChannelDetailsDrawerSlotProps) => (4  <dialog open={isOpen} onClose={() => onOpenChange(false)}>5    <h2>{activeChannel.name}</h2>6    <p>{conversationInfo?.groupDescription ?? "Direct message"}</p>7  </dialog>8);

Avatar

tsx
1import type { AvatarSlotProps } from "@realtimexsco/live-chat";2 3const MyAvatar = ({ src, name, size = "md", isOnline, showOnline }: AvatarSlotProps) => (4  <div className="relative">5    <img src={src || undefined} alt={name} className={`avatar avatar--${size}`} />6    {showOnline && isOnline && <span className="online-dot" />}7  </div>8);

MessageItem (custom bubble) - use MessageLexicalViewer to keep rich-text formatting:

tsx
1import type { MessageItemSlotProps } from "@realtimexsco/live-chat";2 3const MyMessageItem = ({ isSender, message, createdAt }: MessageItemSlotProps) => (4  <div className={isSender ? "text-right" : "text-left"}>5    <div className={isSender ? "bubble-sent" : "bubble-received"}>{message}</div>6    <time className="text-xs opacity-60">{createdAt}</time>7  </div>8);

ChannelListItem

tsx
1import type { ChannelListItemSlotProps } from "@realtimexsco/live-chat";2 3const MyRow = ({ channel, isActive, onSelect }: ChannelListItemSlotProps) => (4  <button type="button" className={isActive ? "row-active" : "row"} onClick={() => onSelect(channel)}>5    {channel.name}6    {channel.unreadCount ? ` (${channel.unreadCount})` : ""}7  </button>8);

MessageInput

tsx
1import type { ChannelMessageBoxSlotProps } from "@realtimexsco/live-chat";2 3const MyInput = ({ onSendMessage, replyTo, onCancelReply }: ChannelMessageBoxSlotProps) => {4  const [text, setText] = useState("");5  return (6    <div>7      {replyTo && <button type="button" onClick={onCancelReply}>Cancel reply</button>}8      <input value={text} onChange={(e) => setText(e.target.value)} />9      <button type="button" onClick={() => { onSendMessage?.(text, [], replyTo); setText(""); }}>10        Send11      </button>12    </div>13  );14};

Note

Custom slots for calls, attachments, threads and canvas should honor the workspace admin's settings - see Admin permissions.

Wrapping package defaults

Every built-in slot component is exported, so you can wrap a default with your theme instead of rebuilding it:

tsx
1import { packageDefaultComponents, type ChatComponents } from "@realtimexsco/live-chat";2 3function withTheme<P extends object>(Default: React.ComponentType<P>, className: string) {4  return (props: P) => (5    <div className={className}>6      <Default {...props} />7    </div>8  );9}10 11const components: ChatComponents = {12  MessageItem: withTheme(packageDefaultComponents.MessageItem!, "my-message"),13  ChannelListItem: withTheme(packageDefaultComponents.ChannelListItem!, "my-row"),14  Avatar: MyFullyCustomAvatar, // or DefaultChatAvatar from the package15};

Also exported: packageDefaultComponents (map of every slot default), DefaultChatAvatar and DefaultChannelListItem.

All classNames keys

Merged with package defaults - your classes extend the styling.

KeyUI region
shellOutermost chat wrapper
mainPanelInner rounded panel
sidebarLeft sidebar
loggedUserDetailsProfile block at the top
globalSearchTrigger / globalSearchPopoverSidebar search button / popover
loggedUserSettingsTrigger / loggedUserSettingsDialogAccount settings button / dialog
loggedUserSettingsHeader / loggedUserSettingsContent / loggedUserSettingsAvatarSettings dialog header / body / avatar
pushNotificationTogglePush on/off row
appearanceModeToggleLight/dark toggle row
threadsInboxTrigger / threadsInboxDialogThreads inbox button / dialog
mentionsTrigger / mentionsDialogMentions button / page
channelList / channelListItem / channelListItemActiveConversation list / row / selected row
channelHeader / headerLeft / headerRightHeader bar and its sections
headerCallActions / phoneCallButton / videoCallButtonCall buttons
messageList / messageItemMessages scroll area / row wrapper
messageBubble / messageBubbleSender / messageBubbleReceiverBubbles (both / outgoing / incoming)
messageInput / messageInputTextareaComposer container / textarea
avatar / avatarFallbackAvatar wrapper / initials fallback
dateDivider“Today” / date separator (also the jump-to-date button)
emptyStateNo-messages placeholder
forwardModalForward dialog
chatPanelAlertsError/info alert bar
drawer / drawerContentDrawer overlay/shell / panel body
sectionItemBordered rows in drawers (files, media, lists)
searchOverlayIn-header search results
tsx
1classNames={{2  shell: "chat-container-root",3  channelListItemActive: "bg-indigo-50 dark:bg-indigo-500/10",4  messageBubbleSender: "rounded-2xl shadow-sm",5  messageBubbleReceiver: "rounded-2xl",6  drawer: "z-[60]",7}}

Events (callback props)

Event-style behavior comes through callback props and hooks rather than a global event emitter:

CallbackFires whenWhere
onColorModeChangeLight/dark mode changesChatMain
onDateTimePreferencesChangeThe user changes locale / time zone / 12-24hChatMain
onErrorDismiss / onInfoDismissThe alert banner is dismissedChatMain
onPhoneCall / onVideoCallCall buttons pressed (replaces built-in calling)features
onUnreadCountChangeThe workspace unread summary changesChatMain / Chat
onSessionEndAn admin ended the sessionChatMain / Chat
onMessageReceivedA message arrives in the backgroundChat (provider)
onSocketConnected / onSocketErrorSocket connects / errorsChat (provider)
onSendMessageThe composer submits a messageMessageInput slot
onSelectA conversation row is clickedChannelListItem slot
onOpenChangeA drawer or modal opens/closesDrawer slots
PreviousConfigurationNextHooks

On this page

Default component fallbackWhich slots are wiredAdvanced slotsAll component slotsCode examples for common slotsWrapping package defaultsAll classNames keysEvents (callback props)
Back to top