import { MessageActions, Search } from "stream-chat-react";Upgrade to v14
stream-chat-react@14 keeps the core chat concepts from v13, but it significantly redesigns component structure, UI override points, and several low-level customization APIs.
If your app uses the default SDK UI with minimal customization, the upgrade is usually straightforward. If you override components, rely on context helper HOCs, customize message input behavior, or theme internal DOM selectors, plan for a focused migration pass.
Quick Migration Checklist
- Upgrade package versions and align
stream-chat. - Replace removed imports and stop importing
MessageActionsorSearchfromstream-chat-react/experimental. - Move UI overrides off
ChannelandChannelListand intoWithComponents/ComponentContext. - Migrate message actions, edit flows, and custom message UIs to the v14
MessageActionsandMessageComposermodel. - Rename old
MessageInput*imports toMessageComposer*, and update custom composer code to useMessageComposerContextplus the dedicated composer hooks. - Review attachment, gallery, reaction, poll, thread, and modal customizations for renamed props and components.
- Remove
/v2from anystream-chat-react/dist/css/*style imports and audit CSS selectors, snapshots, and DOM queries against the new markup.
Import And Entrypoint Changes
The most common direct renames are:
| v13 | v14 |
|---|---|
MessageDeleted | MessageDeletedBubble |
MessageOptions | MessageActions |
MessageInput | MessageComposer |
MessageInputFlat | MessageComposerUI |
MessageInputContext | MessageComposerContext |
useMessageInputContext | useMessageComposerContext |
UploadButton | FileInput |
UploadButtonProps | FileInputProps |
ChannelListMessenger | ChannelListUI |
ChannelPreview | ChannelListItem |
ChannelPreviewMessenger | ChannelListItemUI |
ChannelPreviewActionButtons | ChannelListItemActionButtons |
ChannelSearch | Search |
MessageNotification | NewMessageNotification |
ScrollToBottomButton | ScrollToLatestMessageButton |
ReactionsListModal | MessageReactionsDetail |
MessageIsThreadReplyInChannelButtonIndicator | MessageAlsoSentInChannelIndicator |
Modal | GlobalModal |
AddCommentForm | AddCommentPrompt |
EndPollDialog | EndPollAlert |
SuggestPollOptionForm | SuggestPollOptionPrompt |
The following APIs were removed and require a redesign rather than a rename:
FixedHeightMessageEditMessageFormEditMessageModalMessageActionsBoxMessageActionsWrapperCustomMessageActionsListQuotedPollQuotedMessagePreviewHeaderButtonWithSubmenu
Suggested replacements for the most common removals:
| Removed API | Alternative approach in v14 |
|---|---|
FixedHeightMessage | provide a custom VirtualMessage built against the current MessageUIComponentProps contract |
EditMessageForm, EditMessageModal | start editing through messageComposer.initState({ composition: message }), clear with messageComposer.clear(), and customize EditedMessagePreview |
MessageActionsBox, MessageActionsWrapper, CustomMessageActionsList | rebuild custom actions around MessageActions, defaultMessageActionSet, messageActionSet, and ContextMenu |
QuotedPoll, Poll.isQuoted | move quoted-poll rendering to the quoted-message layer with QuotedMessage, QuotedMessagePreview, or QuotedMessagePreviewUI |
QuotedMessagePreviewHeader | customize the full quoted preview through QuotedMessagePreview / QuotedMessagePreviewUI instead of composing around a separate header |
ButtonWithSubmenu | rebuild submenu flows with ContextMenu, ContextMenuButton, ContextMenuHeader, and ContextMenuBackButton |
MessageActions and Search are no longer exported from stream-chat-react/experimental. Import them from the main package:
Legacy stylesheet import paths also need cleanup. If your app still imports styles from stream-chat-react/dist/css/v2/*, switch to the root dist/css/* entrypoints instead:
// v13 / older RC docs
import "stream-chat-react/dist/css/v2/index.css";
// v14
import "stream-chat-react/dist/css/index.css";The last pre-stable cleanup also removed several deprecated aliases that were still hanging around from the v13 surface:
- remove
pinPermissions,PinPermissions,PinEnabledUserRoles, anddefaultPinPermissions - update
usePinHandler(message, notifications?)if you still passed the old permissions argument - replace top-level
UploadButtonimports withFileInput - replace
ChannelListItemUI.latestMessagewithlatestMessagePreview - replace
EmojiPicker.popperOptionswithplacement - update any
InfiniteScroll,LoadMoreButton, orLoadMorePaginatorwrappers from the old alias props tohasNextPage,hasPreviousPage,loadNextPage,loadPreviousPage, andisLoading - remove imports of the old standalone channel-list listener hooks such as
useChannelDeletedListener,useNotificationMessageNewListener, anduseMobileNavigation
The with*Context HOC wrappers were removed. Replace them with hooks inside function components:
import { useMessageContext } from "stream-chat-react";
const CustomMessage = () => {
const { message } = useMessageContext();
return <div>{message.text}</div>;
};Some low-level helper and icon exports were also removed rather than renamed:
- move emoji-only checks from
isOnlyEmojistocountEmojis()ormessageTextHasEmojisOnly() - replace the removed
useAudioController()hook withuseAudioPlayer() - replace
showMessageActionsBox()/shouldRenderMessageActions()with the v14MessageActionsandmessageActionSetflow - replace removed standalone icons such as
ActionsIcon,ReactionIcon,ThreadIcon,MessageErrorIcon,CloseIcon,SendIcon,MicIcon,MessageSentIcon, andMessageDeliveredIconwith the publicIconsset or higher-level components such asMessageActions,SendButton, andMessageStatus - if you relied on
attachmentTypeIconMap, inline your own map or migrate to the newer thread preview components
Examples:
Emoji-only checks:
import { countEmojis, messageTextHasEmojisOnly } from "stream-chat-react";
const emojiOnly = messageTextHasEmojisOnly(message);
const emojiCount = countEmojis(message.text);Message action visibility:
import { MessageActions, defaultMessageActionSet } from "stream-chat-react";
const messageActionSet = defaultMessageActionSet.filter(
({ placement, type }) =>
placement === "quick-dropdown-toggle" || type !== "delete",
);
<MessageActions messageActionSet={messageActionSet} />;If you build a fully custom messageActionSet and still want dropdown actions, include a quick-dropdown-toggle item explicitly. The SDK no longer injects the menu trigger automatically.
Removed standalone icons:
// v13
import { CloseIcon, MessageDeliveredIcon, SendIcon } from "stream-chat-react";// v14
import { IconXmark, MessageStatus, SendButton } from "stream-chat-react";Replace removed low-level icons with the current shared icons or with higher-level components like SendButton and MessageStatus, depending on whether you are rebuilding UI primitives or using the default SDK flows.
Custom attachment icon maps:
import { IconFileBend, IconImages1Alt, IconVideo } from "stream-chat-react";
const attachmentTypeIconMap = {
file: IconFileBend,
image: IconImages1Alt,
video: IconVideo,
};
const AttachmentTypeIcon =
attachmentTypeIconMap[attachment.type] ?? IconFileBend;
<AttachmentTypeIcon />;Search Changes
ChannelSearch was removed. Search now lives in the main package entrypoint, and ChannelList renders it directly when showChannelSearch is enabled.
Before:
import { ChannelList, ChannelSearch } from "stream-chat-react";
import { Search } from "stream-chat-react/experimental";
<ChannelList
additionalChannelSearchProps={{
placeholder: "Search channels and users...",
searchForChannels: true,
}}
ChannelSearch={ChannelSearch}
showChannelSearch
/>;
<Search />;After:
import { ChannelList, Search, WithComponents } from "stream-chat-react";
const CustomSearch = () => (
<Search
exitSearchOnInputBlur
placeholder="Search channels, messages, and users..."
/>
);
<WithComponents overrides={{ Search: CustomSearch }}>
<ChannelList showChannelSearch />
</WithComponents>;Key changes to account for:
- remove direct
ChannelSearchimports - stop using
ChannelList.additionalChannelSearchProps - stop using the removed
ChannelList.ChannelSearchoverride prop - if you customize search inside
ChannelList, move that work toWithComponents/ComponentContextwithSearch,SearchBar,SearchResults, and related search subcomponents - if you rendered the experimental
Searchdirectly, keep renderingSearch, but import it fromstream-chat-react
useChannelListContext() also dropped its old diagnostic parameter. If you still call it as useChannelListContext("MyComponent"), remove that argument and do not rely on the old outside-provider warning side effect:
import { useChannelListContext } from "stream-chat-react";
// v13
const { channels } = useChannelListContext("ChannelSidebar");
// v14
const { channels } = useChannelListContext();Move UI Overrides From Channel To WithComponents
In v13, many UI overrides could be passed directly to Channel. In v14, that forwarded override surface was removed from ChannelProps. Use WithComponents instead.
Before:
import {
Channel,
ChannelHeader,
MessageInput,
MessageList,
Thread,
Window,
} from "stream-chat-react";
// Import your custom override components from your codebase.
<Channel
Input={CustomMessageInput}
Message={CustomMessage}
MessageActions={CustomMessageActions}
Modal={CustomModal}
>
<Window>
<ChannelHeader />
<MessageList />
<MessageInput />
</Window>
<Thread />
</Channel>;After:
import {
Channel,
ChannelHeader,
MessageComposer,
MessageList,
Thread,
Window,
WithComponents,
} from "stream-chat-react";
// Import your custom override components from your codebase.
<WithComponents
overrides={{
MessageComposerUI: CustomMessageComposer,
Message: CustomMessage,
MessageActions: CustomMessageActions,
Modal: CustomModal,
}}
>
<Channel>
<Window>
<ChannelHeader />
<MessageList />
<MessageComposer />
</Window>
<Thread />
</Channel>
</WithComponents>;Important override-key changes:
| v13 override key | v14 migration |
|---|---|
MessageNotification | NewMessageNotification |
ReactionsListModal | MessageReactionsDetail |
MessageOptions | MessageActions |
MessageIsThreadReplyInChannelButtonIndicator | MessageAlsoSentInChannelIndicator |
ChannelPreviewActionButtons | ChannelListItemActionButtons |
ChannelPreviewMessenger | ChannelListItemUI |
FileUploadIcon | AttachmentSelectorInitiationButtonContents |
EditMessageInput | remove old edit-flow override and customize MessageComposerUI / EditedMessagePreview instead |
EditMessageModal | removed |
QuotedPoll | removed |
Channel also changed its empty-state behavior. If there is no active channel, v14 renders EmptyStateIndicator by default. To preserve the old blank behavior, pass:
import { Channel } from "stream-chat-react";
<Channel EmptyPlaceholder={null}>...</Channel>;ChannelList also removed its direct UI override props. List, Preview, Avatar, LoadingIndicator, and LoadingErrorIndicator now come from WithComponents / ComponentContext.
v13 ChannelList API | v14 migration path |
|---|---|
List | ChannelListUI in WithComponents |
Preview | ChannelListItemUI in WithComponents |
Avatar | Avatar in WithComponents |
LoadingIndicator | LoadingIndicator in WithComponents |
LoadingErrorIndicator | LoadingErrorIndicator in WithComponents |
Before:
import {
ChannelList,
ChannelListMessenger,
type ChannelListMessengerProps,
} from "stream-chat-react";
const CustomChannelList = (props: ChannelListMessengerProps) => (
<ChannelListMessenger {...props} />
);
<ChannelList
List={CustomChannelList}
LoadingIndicator={CustomLoadingIndicator}
Preview={CustomChannelListItem}
/>;After:
import {
ChannelList,
ChannelListItemUI,
ChannelListUI,
WithComponents,
} from "stream-chat-react";
const CustomChannelListUI = (props) => <ChannelListUI {...props} />;
const CustomChannelListItem = (props) => <ChannelListItemUI {...props} />;
<WithComponents
overrides={{
Avatar: CustomChannelAvatar,
ChannelListItemUI: CustomChannelListItem,
ChannelListUI: CustomChannelListUI,
LoadingErrorIndicator: CustomLoadingErrorIndicator,
LoadingIndicator: CustomLoadingIndicator,
}}
>
<ChannelList />
</WithComponents>;If you rely on custom selectors, update them too:
.str-chat__channel-list-messenger->.str-chat__channel-list-inner.str-chat__channel-list-messenger__main->.str-chat__channel-list-inner__main- remove reliance on the deleted
.str-chat__channel-list-reactroot class
Message, Actions, And Edit Flow Changes
MessageOptions has been removed. The supported customization path is the v14 MessageActions component and its action-set model.
Before:
import { MessageOptions } from "stream-chat-react";
const CustomMessageActions = () => <MessageOptions />;After:
import {
MessageActions,
defaultMessageActionSet,
useMessageContext,
useMessageComposerController,
} from "stream-chat-react";
const CustomMessageActions = () => {
const actions = defaultMessageActionSet.filter(
({ placement, type }) =>
placement === "quick-dropdown-toggle" || type !== "delete",
);
return <MessageActions messageActionSet={actions} />;
};
const CustomMessage = () => {
const { groupedByUser, handleDelete, message } = useMessageContext();
const messageComposer = useMessageComposerController();
const onDelete = async () => {
try {
await handleDelete({ hardDelete: true });
} catch {
// The SDK already shows its default delete error notification.
}
};
return (
<div data-grouped-by-user={groupedByUser}>
<button
onClick={() => messageComposer.initState({ composition: message })}
>
Edit
</button>
<button onClick={onDelete}>Delete</button>
</div>
);
};Key message-level changes:
customMessageActionswas removed fromMessageContext,Message,MessageList, andVirtualizedMessageList.handleDeletechanged from(event, options?)to(options?).- deleting unsent or network-failed messages through
handleDelete()now removes them locally instead of routing through the server delete path. handleDelete()now rethrows server-side delete failures after the SDK shows its default error notification, so custom delete buttons should usetry/catchwhen they need app-specific recovery.- Custom
Messageoverrides should read values such asgroupedByUser,firstOfGroup, andendOfGroupfromuseMessageContext()instead of expecting them as injected component props. MessageDeletedwas replaced byMessageDeletedBubble.MessageEditedTimestampwas removed. UseMessageEditedIndicatorfor the default edited label + tooltip, or render your ownTimestampagainstmessage.message_text_updated_atif you need a fully custom edited-time UI.MessageErrorTextandMessageErrorIconwere removed. The default failed-send badge now renders through the shared error indicator under.str-chat__message-error-indicator.MessageStatusPropsno longer includeAvatar, and the default status UI no longer renders the old standalone status icons or reader avatar.MessageTextProps.themewas removed, andMessageTextno longer owns quoted-message rendering.MessageTimestampnow defaults to time-only formatting (HH:mm). If you want the old calendar-style default back, overridetimestamp/MessageTimestampinStreami18nor provide a customMessageTimestamp.
Edit-message flows now run through MessageComposer:
- remove
EditMessageForm,EditMessageModal,useEditHandler, andclearEditingState - start editing with
messageComposer.initState({ composition: message }) - cancel or clear the edit state with
messageComposer.clear() - customize the edit preview through
EditedMessagePreview
Message-list floating indicators and unread UI also changed in v14:
MessageNotificationbecameNewMessageNotificationScrollToBottomButtonbecameScrollToLatestMessageButtonMessageListNotificationswas removed from the public surface- client-side notifications now render through
NotificationList MessageListandVirtualizedMessageListnow renderNewMessageNotification,UnreadMessagesNotification, andScrollToLatestMessageButtondirectly- if you previously wrapped the old notification container, move that layout work to
MessageListMainPaneland the direct floating-notification override points UnreadMessagesSeparatornow shows the unread count by default and includes a mark-read button; passshowCount={false}or provide a custom separator if you want the old simpler behavior- the default message action order and action set also changed in v14, so define your own
messageActionSetexplicitly if your UX depends on a stable action menu - the default
markUnreadaction is now limited to foreign messages; it is no longer available on your own messages even when the channel has the requiredread-eventscapability
Message Input And Composer Changes
The public composer surface was renamed from MessageInput* to MessageComposer*. Most composition state now lives in MessageComposer, MessageComposerContext, and the dedicated composer hooks.
Before:
import { useMessageComposerContext } from "stream-chat-react";
const { cooldownRemaining, handleSubmit, setCooldownRemaining } =
useMessageComposerContext();After:
import {
useCooldownRemaining,
useMessageComposerContext,
} from "stream-chat-react";
const CustomComposerFooter = () => {
const { handleSubmit } = useMessageComposerContext();
const cooldownRemaining = useCooldownRemaining();
const isCooldownActive = cooldownRemaining > 0;
return (
<button disabled={isCooldownActive} onClick={handleSubmit}>
Send ({cooldownRemaining})
</button>
);
};Key composer changes:
MessageInput,MessageInputFlat,MessageInputContext, anduseMessageInputContextwere renamed toMessageComposer,MessageComposerUI,MessageComposerContext, anduseMessageComposerContext.additionalMessageInputPropsbecameadditionalMessageComposerProps.handleSubmitnow accepts only an optional event. It no longer acceptscustomMessageDataorSendMessageOptions.useMessageComposerBindings()replaced the older low-level bindings hook name if you build custom composer providers.- The default
CooldownTimeris now a zero-prop component:
import { CooldownTimer } from "stream-chat-react";
<CooldownTimer />;- Custom message data should be added through composer middleware or
messageComposer.customDataManager. LinkPreviewListnow shows one preview by default and no longer suppresses previews while quoting a message.- The default textarea now grows up to 10 rows instead of 1. Pass
maxRows={1}if you need the old behavior. AttachmentSelectorwas redesigned. CustomattachmentSelectorActionSetlogic should be reviewed for the new action model, command submenu support, and cooldown-disabled trigger behavior.- the default attachment selector can now add a
selectCommandaction, and runtime-disabled upload actions are filtered out instead of being shown disabled - the default composer now hides the attachment selector and additional composer actions while a slash command is selected; custom input UIs should decide whether to mirror that command-active layout
- Voice recordings moved out of
AttachmentPreviewListinto a dedicatedVoiceRecordingPreviewSlot. QuotedMessagePreviewHeaderwas removed. If you customized quoted previews, overrideQuotedMessagePrevieworQuotedMessagePreviewUIinstead of trying to swap only the old header fragment.AutocompleteSuggestionItemnow follows the redesigned suggestion-list item contract and should not assume the old internal list-item wrapper.
Attachments, Gallery, And File/Media Changes
Attachment customization changed in a few different layers.
At the high level:
AttachmentProps.GallerybecameAttachmentProps.ModalGalleryAttachmentProps.Medianow usesVideoPlayerProps- image and video grouping now flows through the newer media path instead of the old
gallery/imagesplit
If you render ModalGallery directly, migrate from the old images / index API to the new items-based API:
import { ModalGallery } from "stream-chat-react";
<ModalGallery items={galleryItems} />;Gallery itself is no longer the old thumbnail-grid-plus-lightbox component. In v14 it is the provider-style carousel layer, and it only renders visible UI when a GalleryUI is supplied:
import { Gallery, GalleryUI } from "stream-chat-react";
<Gallery GalleryUI={GalleryUI} items={galleryItems} />;If your old customization wrapped Gallery directly, decide whether you actually want:
ModalGalleryfor the old “thumbnail grid that opens a viewer” behaviorGalleryplus a customGalleryUIfor a custom carousel/viewer implementation
If you use lower-level attachment primitives, review these changes too:
MediaContainernow works withattachmentsinstead of a singleattachment- gallery payloads changed from
imagestoitems - audio attachment props changed from
ogtoattachment CardAudiois no longer re-exported from the package root- native
giphyattachments now stay inline throughGiphy; they do not expand throughModalGallery
FileIcon was simplified:
| v13 | v14 |
|---|---|
filename | fileName |
big, size, sizeSmall, type | removed |
mimeTypeToIcon(type, mimeType) | mimeTypeToIcon(mimeType?) |
Reactions, Polls, And Dialogs
The reactions and polls surfaces changed enough that custom wrappers usually need code changes.
Reactions:
- rename
ReactionsListModaltoMessageReactionsDetail - treat
MessageReactionsDetailas dialog content, not as the old standalone modal component - custom
ReactionSelectorcomponents can no longer rely on the old prop surface such asreactionOptions,latest_reactions,reaction_counts, orAvatar sortReactionDetailsandReactionDetailsComparatorwere removed. UsereactionDetailsSortwith a server-sideReactionSortobject instead.- custom
MessageReactionscomponents should usereaction_groups, notreaction_counts MessageReactionsno longer accepts directreactionOptions; keep reaction-option customization inWithComponents/ComponentContext- if you customize reaction menu contents, review the current
reactionOptionsand quick/extended action model
Polls:
QuotedPollwas removedPoll.isQuotedwas removedAddCommentForm,EndPollDialog, andSuggestPollOptionFormwere renamed toAddCommentPrompt,EndPollAlert, andSuggestPollOptionPrompt- several poll-dialog override components no longer receive
closecallbacks directly and now depend on dialog context
If you previously rendered quoted polls with QuotedPoll or <Poll isQuoted />, move that UI to the quoted-message layer instead of the poll component itself. The default quoted preview already renders poll messages as a compact summary, so integrations that only need a small quoted-poll preview can usually switch to QuotedMessage or QuotedMessagePreviewUI:
import { QuotedMessagePreviewUI } from "stream-chat-react";
<QuotedMessagePreviewUI quotedMessage={quotedMessage} />;If you need a richer quoted-poll card than the default compact preview, override QuotedMessage or QuotedMessagePreview and render quotedMessage.poll inside your own quoted-message UI.
Dialogs:
Modalwas replaced byGlobalModalButtonWithSubmenuwas removed; submenu-style UIs should be rebuilt aroundContextMenu- the old
.str-chat__modal__innerwrapper no longer exists - some prompt-like override components, such as
MessageBouncePrompt, now close through modal context instead of anonCloseprop RecordingPermissionDeniedNotificationno longer receives anonCloseprop and no longer behaves like the old dismissible notification- custom dialog-style overrides that previously relied on explicit
closecallbacks should be reviewed foruseModalContext()-based dismissal instead
Explicit Query Limits
Channel and ChannelList no longer inject the old initial query limits for you. If your app relied on the previous defaults, set them explicitly:
import { Channel, ChannelList } from "stream-chat-react";
<Channel channelQueryOptions={{ messages: { limit: 20 } }}>
...
</Channel>
<ChannelList options={{ limit: 30 }} />Sidebar State Is Now App-Owned
Chat no longer owns sidebar open/closed state. The old initialNavOpen prop and the navOpen, openMobileNav, and closeMobileNav values from useChatContext() were removed.
Keep that state in your app and inject any toggle UI through HeaderStartContent or HeaderEndContent:
import { useState } from "react";
import {
Channel,
ChannelHeader,
MessageComposer,
MessageList,
Window,
WithComponents,
} from "stream-chat-react";
const AppLayout = () => {
const [sidebarOpen, setSidebarOpen] = useState(false);
const SidebarToggle = () => (
<button onClick={() => setSidebarOpen((open) => !open)} type="button">
{sidebarOpen ? "Close sidebar" : "Open sidebar"}
</button>
);
return (
<WithComponents overrides={{ HeaderStartContent: SidebarToggle }}>
<Channel channel={channel}>
<Window>
<ChannelHeader />
<MessageList />
<MessageComposer />
</Window>
</Channel>
</WithComponents>
);
};The same change affects ChannelHeader: the old MenuIcon prop was removed, so any built-in sidebar toggle customization should move to HeaderStartContent.
For a complete app-owned implementation, see the Collapsible Sidebar cookbook.
Header And Thread Changes
ChannelHeader.livewas removed. The default header also no longer renderschannel.data.subtitle.ThreadHeaderno longer acceptsoverrideImage.ThreadHeadersubtitle and close-button behavior changed.- in v13 the SDK only exposed the message-list typing indicator. In v14 typing state is split across two surfaces:
TypingIndicatoris the message-list indicator. In v14 it renders an avatar stack plus animated dots, and it receivesscrollToBottomplus optionalisMessageListScrolledToBottomso it can stay pinned to the latest message while someone is typing.TypingIndicatorHeaderis the header subtitle indicator.ChannelHeadernow switches its subtitle from online-status text toTypingIndicatorHeaderwhen someone is typing in the channel, andThreadHeaderdoes the same for thread typing state.
- custom
TypingIndicatoroverrides only affect the message-list indicator. If you ship a customChannelHeaderorThreadHeaderand want to preserve the new default typing behavior, renderTypingIndicatorHeaderin those custom headers yourself. ChatView.ThreadAdapterno longer stays blank when no thread is selected. It now renders an empty-state placeholder after thread state is ready.- if you want to preserve the old blank thread-pane behavior, wire
ThreadProvidermanually or overrideEmptyStateIndicatorin that scope with a component that rendersnull ChatView.Selectornow defaults to icon-only buttons. PassiconOnly={false}to preserve the old labeled UI:
For example:
import { TypingIndicatorHeader } from "stream-chat-react";
const CustomChannelHeader = () => {
return (
<div>
<div>General</div>
<div>
{hasTyping ? (
<TypingIndicatorHeader />
) : (
<span>{onlineStatusText}</span>
)}
</div>
</div>
);
};
const CustomThreadHeaderSubtitle = () => (
<div>
{hasThreadTyping ? (
<TypingIndicatorHeader threadList />
) : (
<span>
{threadDisplayName} · {replyCount} replies
</span>
)}
</div>
);In the example above, hasTyping, onlineStatusText, hasThreadTyping, threadDisplayName, and replyCount come from your own header logic or the relevant SDK contexts/hooks.
import { ChatView } from "stream-chat-react";
<ChatView.Selector iconOnly={false} />;Smaller Custom-Component Prop Changes To Review
If your app overrides low-level UI pieces, review these prop-surface changes as part of the migration:
| Area | v13 | v14 |
|---|---|---|
Avatarimage, name -> imageUrl, userName, required size | <Avatar image={url} name={name} /> | <Avatar imageUrl={url} size={32} userName={name} /> |
ChannelAvatar / GroupAvatargroupChannelDisplayInfo -> displayMembers, required size, automatic overflow | <ChannelAvatar groupChannelDisplayInfo={info} /> | <ChannelAvatar displayMembers={members} size={40} /> |
channel display helpersgetDisplayTitle, getDisplayImage -> useChannelDisplayName(), getChannelDisplayImage() | getDisplayImage(channel) | getChannelDisplayImage(channel) |
TypingIndicatorPropsthreadList? -> scrollToBottom, optional isMessageListScrolledToBottom, threadList | <TypingIndicator threadList /> | <TypingIndicator scrollToBottom={scrollToBottom} threadList /> |
ThreadHeaderPropsoverrideImage, overrideTitle -> overrideTitle only | <ThreadHeader overrideImage={img} overrideTitle="Thread" /> | <ThreadHeader overrideTitle="Thread" /> |
MessageEditedTimestampremoved in favor of MessageEditedIndicator | <MessageEditedTimestamp calendar open={open} /> | <MessageEditedIndicator calendar /> |
MessageStatusPropsinjected Avatar removed | const MyStatus = ({ Avatar, ...props }) => ... | const MyStatus = (props) => ... |
MessageTextPropstheme removed | <MessageText message={message} theme={theme} /> | <MessageText message={message} /> |
PinIndicatorPropst removed; read translations from context instead | ({ message, t }) => t("Pinned") | ({ message }) => { const { t } = useTranslationContext(); ... } |
suggestion UserItemPropsinjected Avatar removed | ({ entity, Avatar }) => <Avatar user={entity} /> | ({ entity }) => <UserContextMenuButton userName={entity.name} /> |
FileIconPropssize and mode props replaced by fileName, mimeType, optional className | <FileIcon filename="a.pdf" size={32} /> | <FileIcon fileName="a.pdf" mimeType="application/pdf" /> |
Two smaller behavior changes are easy to miss during this pass:
useChannelPreviewInfo()now returns stable empty group info instead ofnull/undefined, so any “is this a group channel?” checks based ongroupChannelDisplayInfotruthiness should be revieweduseLatestMessagePreview()now reports nativegiphyattachments astype: "giphy", so custom channel previews should not assume all GIF-like content comes through theimagebranch- custom typing indicators should accept the new scroll-related props if they need to preserve the default “stay pinned to latest message while typing” behavior, and custom headers should render
TypingIndicatorHeaderif they want the new subtitle-level typing state
Styling, DOM, And Snapshot Review
v14 includes a visible markup refresh. If you maintain custom CSS, DOM queries, or visual snapshots, run a manual review over at least these areas:
- channel header markup and sidebar toggle selectors
- message composer wrappers and controls under
str-chat__message-composer* - avatar markup, initials, and online-status badge selectors
- channel preview and thread list item markup, including the move from
aria-selectedtoaria-pressed - message reactions selector/detail markup
- unread separator, new-message notification, and scroll-to-latest button markup
- modal and dialog markup, especially removal of
.str-chat__modal__inner - date separator visuals and any custom
position/unreadassumptions - attachment selector, link preview cards, and drag-and-drop overlay markup
- loading skeletons and send-to-channel checkbox styling
If your theme relies on the old jump-to-latest CSS variables or old internal wrappers, expect to update those selectors. The default DateSeparator also no longer renders the old line-based position variants or the old unread-prefixed label, so provide a custom DateSeparator if your UI still depends on those visuals.
- Quick Migration Checklist
- Import And Entrypoint Changes
- Search Changes
- Move UI Overrides From Channel To WithComponents
- Message, Actions, And Edit Flow Changes
- Message Input And Composer Changes
- Attachments, Gallery, And File/Media Changes
- Reactions, Polls, And Dialogs
- Smaller Custom-Component Prop Changes To Review
- Styling, DOM, And Snapshot Review