RealTimeX
Start For Free

RealTimeX

Package Documentation

IntroductionPrerequisitesInstallationProject setupQuick start
Package structureConfigurationComponentsHooksAPI integrationSocket events
OverviewReplace entire UIReplace componentsStyle with classNamesThemes & localePhone & video callingIn-call control barFeatures
TroubleshootingFAQVersion historyBest practicesSupport & contact

Package Reference

Component Documentation

The chat UI is built from named component slots. Every wired slot has a package default; you only pass the components you want to replace via the components prop on ChatMain (or uiConfig.components on Chat). Import the matching *SlotProps type from the package for full TypeScript support.

Which slots are wired

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
MembersDrawer / AdminsDrawer / PermissionDrawerChannelHeaderViewPackage default
ChannelDetailsDrawer / AttachmentsDrawerChannelHeaderViewPackage default
PinnedMessagesDrawer / StarredMessagesDrawerChannelHeaderViewPackage default

Note

To replace the entire header and every drawer inside it at once, pass components.ChannelHeader with your own implementation.

Advanced slots (customize by replacing a parent)

SlotCustomize by replacing
MessageItemMessageList (render your own list of custom items)
MessageToolbar, MessageStatus, MessageAttachments, MessageReactions, MessageReplySnippetMessageItem or MessageList
HeaderSearchOverlayHeaderRightSide or ChannelHeader
ChatThemeToggle, ChatDateTimeSettings, ChatSocketStatusParent header/sidebar or ChannelHeader

Full component slot reference

SlotReplacesProp type
ChatLayoutEntire chat shellChatLayoutSlotProps
LoggedUserDetailsSidebar profile + new chatLoggedUserDetailsSlotProps
ChannelListConversation listChannelListSlotProps
ChannelListItemOne conversation rowChannelListItemSlotProps
ChannelHeaderFull header barChannelHeaderSlotProps
HeaderLeftSideBack button, name, pin/starHeaderLeftSideSlotProps
HeaderRightSideSearch, menu, settingsHeaderRightSideSlotProps
HeaderCallActionsPhone + video button groupHeaderCallActionsSlotProps
PhoneCallButtonVoice call buttonPhoneCallButtonSlotProps
VideoCallButtonVideo call buttonVideoCallButtonSlotProps
HeaderSearchOverlayMessage search dropdownHeaderSearchOverlaySlotProps
MessageListScrollable messages areaActiveChannelMessagesSlotProps
MessageItemSingle message bubbleMessageItemSlotProps
MessageInputComposerChannelMessageBoxSlotProps
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
AvatarAvatar everywhereAvatarSlotProps
MessageToolbarReply/forward/edit menuMessageToolbarSlotProps
MessageStatusSent/delivered/read ticksMessageStatusSlotProps
MessageAttachmentsFiles/images in bubbleMessageAttachmentsSlotProps
MessageReactionsEmoji chipsMessageReactionsSlotProps
MessageReplySnippetQuoted reply in bubbleMessageReplySnippetSlotProps
ChatThemeToggleLight/dark toggle{ className?: string }
ChatDateTimeSettingsDate/time settings buttonRecord<string, never>
ChatSocketStatusConnection indicator{ className?: string }

Default component fallback behavior

Customization is opt-in per slot — you never need to register every component. Internally, every wired slot calls resolveComponent(slotName, PackageDefault): if you passed a custom component it is used; otherwise the package default renders.

You passResult
No components prop at allFull default chat UI
components={{ Avatar: MyAvatar }}Custom avatar only; all other slots use defaults
components={{ MembersDrawer: undefined }}Treated as not passed — default drawer renders
classNames={{ sidebar: '...' }}Your classes merge on top of the package defaults

Wrapping package defaults instead of rebuilding

Use packageDefaultComponents when you only need to restyle or wrap a default component rather than rebuild it:

tsx
import {
  packageDefaultComponents,
  type ChatComponents,
} from "@realtimexsco/live-chat";

function withTheme<P extends object>(
  Default: React.ComponentType<P>,
  className: string
): React.ComponentType<P> {
  return (props) => (
    <div className={className}>
      <Default {...props} />
    </div>
  );
}

const components: ChatComponents = {
  MessageItem: withTheme(packageDefaultComponents.MessageItem!, "my-message"),
  ChannelListItem: withTheme(packageDefaultComponents.ChannelListItem!, "my-row"),
};

Code examples for common slots

The examples below show practical replacements for the slots teams customize most often. Pass only the keys you need on components — every omitted slot keeps the package default. For wrapping defaults instead of rebuilding them, use packageDefaultComponents above.

Avatar

tsx
import type { AvatarSlotProps } from "@realtimexsco/live-chat";

const MyAvatar = ({ src, name, size = "md", isOnline, showOnline }: AvatarSlotProps) => (
  <div className="relative">
    <img src={src || undefined} alt={name} className={`avatar avatar--${size}`} />
    {showOnline && isOnline && <span className="online-dot" />}
  </div>
);

MessageItem (custom bubble)

tsx
import type { MessageItemSlotProps } from "@realtimexsco/live-chat";

const MyMessageItem = ({ isSender, message, createdAt }: MessageItemSlotProps) => (
  <div className={isSender ? "text-right" : "text-left"}>
    <div className={isSender ? "bubble-sent" : "bubble-received"}>{message}</div>
    <time className="text-xs opacity-60">{createdAt}</time>
  </div>
);

ChannelListItem

tsx
import type { ChannelListItemSlotProps } from "@realtimexsco/live-chat";

const MyRow = ({ channel, isActive, onSelect }: ChannelListItemSlotProps) => (
  <button
    type="button"
    className={isActive ? "row-active" : "row"}
    onClick={() => onSelect(channel)}
  >
    {channel.name}
    {channel.unreadCount ? ` (${channel.unreadCount})` : ""}
  </button>
);

MessageInput (composer)

tsx
import type { ChannelMessageBoxSlotProps } from "@realtimexsco/live-chat";

const MyInput = ({ onSendMessage, replyTo, onCancelReply }: ChannelMessageBoxSlotProps) => {
  const [text, setText] = useState("");
  return (
    <div>
      {replyTo && <button type="button" onClick={onCancelReply}>Cancel reply</button>}
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <button
        type="button"
        onClick={() => { onSendMessage?.(text, [], replyTo); setText(""); }}
      >
        Send
      </button>
    </div>
  );
};

MembersDrawer (full custom drawer)

Register it on ChatMain: components={{ MembersDrawer: MyMembersDrawer }}. Remove that key to restore the package default drawer.

tsx
import { useChatStore, type MembersDrawerSlotProps } from "@realtimexsco/live-chat";

const MyMembersDrawer = ({
  isOpen,
  onOpenChange,
  conversationInfo,
}: MembersDrawerSlotProps) => {
  const manageGroupMembers = useChatStore((s) => s.manageGroupMembers);
  const participants = conversationInfo?.participants ?? [];

  if (!isOpen) return null;

  return (
    <aside className="members-drawer">
      <header>
        <h2>Members ({participants.length})</h2>
        <button type="button" onClick={() => onOpenChange(false)}>Close</button>
      </header>
      <ul>
        {participants.map((p, i) => {
          const id = typeof p === "string" ? p : p._id;
          const name = typeof p === "string" ? "User" : p.name;
          return <li key={`${id}-${i}`}>{name}</li>;
        })}
      </ul>
    </aside>
  );
};

All classNames keys

Class overrides merge on top of package defaults rather than replacing components entirely:

KeyUI region
shellOutermost chat wrapper
mainPanelInner rounded panel
sidebarLeft sidebar
loggedUserDetailsProfile block at top
channelList / channelListItemConversation list / each row
channelListItemActiveSelected conversation row
channelHeader / headerLeft / headerRightHeader bar and its sections
headerCallActions / phoneCallButton / videoCallButtonCall button group and buttons
messageList / messageItemMessages scroll area / row wrapper
messageBubble / messageBubbleSender / messageBubbleReceiverBubble styling, both / outgoing / incoming
messageInput / messageInputTextareaComposer container / textarea
avatar / avatarFallbackAvatar wrapper / initials fallback
dateDivider"Today" / date separator
emptyStateNo messages placeholder
forwardModalForward dialog
chatPanelAlertsError/info alert bar
drawer / drawerContentDrawer overlay/shell / panel body
searchOverlayIn-header search results
tsx
classNames={{
  shell: "chat-container-root",
  channelListItemActive: "bg-indigo-50 dark:bg-indigo-500/10",
  messageBubbleSender: "rounded-2xl shadow-sm",
  messageBubbleReceiver: "rounded-2xl",
  drawer: "z-[60]",
}}

Events

The package does not expose a separate global event-emitter API for events — instead, event-style behavior is delivered through callback props and hook subscriptions. The table below summarizes the callback-based "events" available.

Event (callback prop)Fires whenWhere
onColorModeChangeThe light/dark mode changesChatMain
onDateTimePreferencesChangeThe user changes locale/timezone/12-24h settingsChatMain
onErrorDismiss / onInfoDismissThe alert banner is dismissedChatMain
onPhoneCall / onVideoCallThe user taps the call button (features prop)ChatMain
onMessageReceivedA new message arrives in the backgroundChat (provider)
onSocketConnectedThe socket connection is establishedChat (provider)
onSocketErrorThe socket connection errorsChat (provider)
onSendMessageThe composer submits a message (slot prop)MessageInput slot
onSelectA conversation row is clicked (slot prop)ChannelListItem slot
onOpenChangeA drawer or modal opens/closes (slot prop)Drawer slots
PreviousConfigurationNextHooks

On this page

Which slots are wiredAdvanced slots (customize by replacing a parent)Full component slot referenceDefault component fallback behaviorWrapping package defaults instead of rebuildingCode examples for common slotsAvatarMessageItem (custom bubble)ChannelListItemMessageInput (composer)MembersDrawer (full custom drawer)All classNames keysEvents