v15.0.0-rc.1
Pre-release15.0.0-rc.1 (2026-09-29)
β BREAKING CHANGES
- port the tutorial alignment (#3251) to v15, with panel-aware workspace navigation (#3301)
- MessageComposer: the
preventClearingOnUnmountprop is removed from
MessageComposer. Drop it. If you relied on unmounting to clear a
composer supplied throughMessageComposerControllerProvider, call
clear()on it yourself when your UI is done with it. ChannelListItemProps.channelUpdateCountis removed.
The unread badge now updates without it.ChannelListItemProps.keyis removed. React's own
keyprop is unaffected.ChannelListItemProps.watchersis removed. It had no
effect in v15.- adopt stream-chat's branded TimestampNS (#3297)
- Attachment:
useSendMessageFnanduseUpdateMessageFnare removed. Call
messageComposer.send()andmessageComposer.update()instead. They resolve
'sent' | 'nothing-to-send' | 'failed'rather than a boolean, so a truthiness
check on the result now passes for every outcome β compare against'sent'. - Attachment: the two message submission failure notifications changed key.
messageComposer.sendMessageFn.sendMessageRequestFailed.textis now
notification.messageSendFailed, and
messageComposer.updateMessageFn.editMessageRequestFailed.textis now
notification.messageUpdateFailed. A typed custom translation dictionary fails
to compile until both are renamed. - Attachment:
RetryHandlertakesOmit<OperationParams<'retry'>, 'message'>
instead ofRetrySendMessageWithLocalUpdateParams, which stream-chat removed in
GetStream/stream-chat-js#1882. Code that names the old type must be updated. - the
stream-chatpeer range moves to^10.0.0-rc.12. That release removes
connection.changed, changesclient.wsConnectionfrom the socket itself to a wrapper, and moves
the socket's settings into configuration. See GetStream/stream-chat-js PR 1859 for the full list;
an application on an earlierstream-chatwill not typecheck against this version. - connectivity is read from state stores rather than events. Code that listened for
connection.changedβ to render its own banner, to refetch on reconnect β must subscribe to
client.wsConnection.stateorclient.networkConnection.stateinstead, or use the new
useWSConnectionState/useNetworkConnectionStatehooks. Nothing in this package dispatches or
forwards the old event. Note the two stores name their fields differently: the socket'sisHealthy
is always a boolean, while the network'sisOnlineisboolean | undefined, whereundefined
means no platform reporter has reported yet and must be tested with=== false. Channelno longer reloads its channel onconnection.recovered. The client
reloads every active channel itself and dispatches the event afterwards, so handling it here
reloaded each open channel twice per reconnect. Anything relying on the component to refresh a
channel after a reconnect should rely on the client instead.- a socket drop on a working network now renders "Reconnectingβ¦" rather than
"Waiting for networkβ¦", from the newchat.reportLostConnection.reconnecting.textkey. Tests and
dictionaries keyed on the old copy for that case need updating; thewaitingNetworkkey is
unchanged and still used when the device network is down. The delay before either banner appears
now comes fromclient.wsConnection.config.offlineNotificationDisplayDelayMs(5s default) rather
than from a fixed hold inside the client, so changing that config changes when the banner shows.
Thesystem:network:connection:lostnotification type is unchanged. Threadrequires athreadprop and renderschildren.
additionalMessageComposerProps,additionalMessageListProps,
additionalParentMessageProps,additionalVirtualizedMessageListProps,autoFocusand
virtualizedare removed β pass those props to the children you render.Thread
provides the thread itself, so it no longer needs to be wrapped inThreadProvider. The
str-chat__thread--virtualizedclass is gone;Threadcannot know which list you chose.Thread.enableDateSeparatoris removed with nothing in its place. A
MessageListin a thread now follows its own default and shows date separators unless
you passwithDateSeparator={false}.disableDateSeparatoris renamed towithDateSeparatoron
MessageList(defaulttrue, wasdisableDateSeparator={false}) and
VirtualizedMessageList(defaultfalse, wasdisableDateSeparator={true}). The rename
also reachesprocessMessages,useEnrichedMessages,FloatingDateSeparator,
useFloatingDateSeparatoranduseFloatingDateSeparatorMessageList.- the
headprop is removed fromMessageListand
VirtualizedMessageList. Both render the thread's parent message from context; override
it with aThreadHeadcomponent onComponentContext. ThreadHeaderPropsis down tooverrideTitle.threadcomes from the
thread in context, andcloseThreadgoes through workspace navigation β customize it
centrally viaChatView'sderiveWorkspaceNavigation, or call the newuseCloseThread()
hook from a header of your own.ComponentContext.ThreadHeaderis removed; compose the
header you want.ThreadSlotwraps its children in<Thread>instead of letting them
replace the whole panel, mirroringChannelSlot. Markup that must sit outside the thread
container moves outsideThreadSlot; anything needing thread context must sit inside.Threadno longer rebuilds its subtree when the thread changes. A
custom component rendered insideThreadthat relied on being remounted per thread β to
reset local state, or to re-run a mount effect β must depend on the thread instead. The
SDK's own message list does this by keying on the thread;WithAudioPlaybackby taking
playbackScope.LegacyThreadContextand itslegacyThreadvalue are removed. The
thread composer resolves its composition context from theThreadinstance in
ThreadContext.- MessageActions: the
messageActionsprop is removed fromChannel,
MessageList,VirtualizedMessageList,ThreadandMessage. Filter
defaultMessageActionSetand pass the result asmessageActionSet
instead. - MessageActions:
MESSAGE_ACTIONS,OPTIONAL_MESSAGE_ACTIONS,
getMessageActions(), and theMessageActionsArrayandCapabilities
types are no longer exported. Use the newDefaultMessageActionType
union for the action types the SDK ships. - MessageActions:
MessageContextno longer carriesgetMessageActions
oractionsEnabled. The rendered action set is decided by
messageActionSetand each item's ownisVisiblepredicate.
Co-authored-by: Claude Opus 5 noreply@anthropic.com
- AudioPlayback:
ChannelProps.allowConcurrentAudioPlayback,
ThreadProps.allowConcurrentAudioPlaybackand
WithAudioPlaybackProps.allowConcurrentPlayback
are removed, with no replacement. If you passedtrue, delete it:
starting a player now pauses
whichever was playing and takes the shared element over. If you passed
falseor nothing, the
only change is that playback is exclusive across surfaces rather than
within one. - AudioPlayback:
useActiveAudioPlayer()now reports the app-wide
active player rather than the
calling surface's. - Channel:
ChannelProps.channelis now required and
EmptyPlaceholderis removed.
Render the exportedChannelPlaceholderto fill the same
.str-chat__channellayout slot
while no channel is selected. - Channel:
Channelno longer queries the channel;
initializeOnMountand
channelQueryOptionsare removed. Whoever supplies the channel
initializes it β use the
exportedgetChannel({ channel, client }), which watches and
de-duplicates concurrent calls.
Channels fromChannelListor anyqueryChannelscall arrive watched
already.Channelno
longer rendersLoadingIndicatororLoadingErrorIndicatorfor a query
it does not make, and
an uninitialized channel renders as an empty channel with no error. - Channel:
Channelno longer writesdocument.title, and
activeUnreadHandleris
removed. Seeexamples/vite/src/DocumentTitleManagerfor a working
replacement in application
code. - Channel: the
channel.channelMissing.texttranslation key is
removed. This is a
compile error (TS2353) for any dictionary typed with the exact
TranslationDictionaryβ
delete the entry from custom locale files. - Channel:
ChatContext.latestMessageDatesByChannelsis removed.
Slow-mode cooldown now
lives onchannel.cooldownTimer(useCooldownRemaining/
useIsCooldownActive), and the
channel's latest message on
channel.messagePaginator.aggregateState.lastMessage. - Channel:
Channelno longer jumps to a message focused from
search. Selecting a search
result callschannel.messagePaginator.jumpToMessage(id)directly, and
the highlight is read
fromchannel.messagePaginator.messageFocusSignal. A custom message
list that relied on
Channelperforming the jump must calljumpToMessageitself. - i18n: consume the shared runtime from @stream-io/i18n (#3284)
- move to timestamps in response models (#3278)
- request-handler props are removed from
Channeland
Thread.
doSendMessageRequest,doUpdateMessageRequest,
doDeleteMessageRequestand
doMarkReadRequestare gone, along withuseChannelRequestHandlers,
useThreadRequestHandlersanduseEditMessageHandler. Register once on
the client:
// v14
<Channel channel={channel} doSendMessageRequest={mySend}>
// v15
client.config.set({
channel: {
requestHandlers: {
sendMessageRequest: async ({ localMessage, message, options }) => ({
message: await mySend(message, options),
}),
},
},
});
Three differences: handlers take a single params object and return { message }; thread
flows register under the thread key; and registration is per CLIENT,
not per mounted
subtree β the one thing the props could do that this cannot. Per-channel
behaviour now
needs a branch inside one handler on the cid it receives.
useChannelConfigreturns the channel's RESOLVED
configuration instead
of the channel type's raw server config, so field names change:
channelConfig?.typing_events β channelConfig?.typingEvents.enabled
channelConfig?.read_events β channelConfig?.readEvents.enabled
channelConfig?.replies β channelConfig?.replies.enabled
channelConfig?.user_message_reminders β
channelConfig?.userMessageReminders.enabled
channelConfig?.commands β channelConfig?.availableCommands
Every gate is now the server flag ANDed with what is registered through
client.config,
which is what the client actually enforces. The hook also subscribes:
these were getters,
so a client.config.set() previously never reached the screen. No
component reads
channel.serverConfig any more.
useAttachmentManagerStatereplaces
hasCustomDoUploadRequestwith
customCdn, and gainsattachmentsEnabled,locationEnabledand
pollsEnabled.- the per-domain stores on
channel.stateare removed β
readStore,
typingStore,membersStore,watcherStore,ownCapabilitiesStore,
mutedUsersStore.
Subscribe tochannel.stateitself; the selector is unchanged, because
ChannelStateData
is flat and keeps the same top-level keys:
useStateStore(channel.state.ownCapabilitiesStore, sel) β
useStateStore(channel.state, sel)
WatcherState is renamed ChannelWatchState, and
channel.disconnected is renamed
channel.pendingDisposal (removed outright β no deprecated alias).
AIStatesis no longer exported from
stream-chat-reactβ import it
fromstream-chat. The values are unchanged, but it is now
literal-typed, so an inferred
array of its members no longer accepts the wideAIState:
const STOPPABLE: readonly AIState[] = [AIStates.Thinking,
AIStates.Generating];
<Channel>marks its channel active while mounted and
reloads it on
connection.recovered. The client skips re-seeding an active channel's
message list on
hydration and reconnect, so the consumer owns that window. A custom
channel surface that
does not render<Channel>must callchannel.activate()/
deactivate()and
channel.reload()itself, or its list goes stale after a reconnect.- i18n: adopt the shared i18n layer from stream-chat/i18n (#3271)
- context: - Context hooks no longer accept a
componentNameargument.
useChatContext('X')and friends stop type-checking; drop the argument.
- Required contexts throw when used outside their provider, instead
of logging a warning and returning{}. Affects the 14 hooks listed
above. Components must be rendered within the provider they read from β
e.g. anything callinguseChatContextneeds a<Chat>ancestor. useChannelInstanceContextreturns
Partial<ChannelInstanceContextValue>βchannelis now typed as
possiblyundefined.ChatViewContexthas no default value (typed| undefined), so
useContext(ChatViewContext)may returnundefined.MessageComposerContextis typed asMessageComposerContextValue | undefined, correcting a narrower declaration that was papered over
with casts.- The gallery header no longer uses the
ComponentContext.MessageTimestampoverride; it renders the item's
own timestamp.
π Implementation details
Parameter removed from all 7 hooks and their 138 call sites.
Missing-provider behaviour also normalized, since src/ had three
competing conventions (warn + {} as T, throw, silent cast). The {} as T fallback deferred failures to a downstream Cannot read properties of undefined several frames away. Everything now goes through
requireContext (src/context/requireContext.ts):
- Required β throw, naming hook and provider:
useChatContext,
useMessageContext,useMessageComposerContext,
useMessageListContext,useVirtualizedMessageListContext,
useMessageBounceContext,usePollContext,useDialogManager,
useSearchContext,useSearchSourceResultsContext,
useContextMenuContext,useChannelListItemContext,
useGalleryContext,useChatViewContext - Optional by design β keep default, honest type:
useTranslationContext,useComponentContext,
useChannelInstanceContext(nowPartial),useModalContext,
useAriaLiveAnnouncer,useMessageTranslationViewContext - Renders both inside and outside a provider β read the raw context:
Audio,VoiceRecording,CardAudio,Timestamp,
MessageRepliesCountButton,MessageComposerUI,ModalGallery
Two changes were needed before their hooks could throw:
WithDragAndDropUploaddetected the composer via
Object.keys(ctx).length > 0; now uses a
useIsWithinMessageComposerContext()predicate. Adds the test file this
component lacked.GalleryHeaderread message context while rendering outside any
MessageProvider.GalleryItemnow carriesuserandcreatedAtβ
wrapping the call sites would be wrong, sinceChannelMediaView
flattens many messages into one list. Adds a
timestamp.GalleryTimestampkey.
ChatViewContext also loses its module-level LayoutController
default, which let unrelated subtrees write to one shared instance.
src/context/__tests__/missingProviderContract.test.tsx pins the
required/optional split.
π¨ UI Changes
The channel-media gallery header now shows a per-item sender and
timestamp, which it previously couldn't display at all.
No screenshots: the demo users on the shared environment have no
channels, and seeding one would write test data others would see.
Covered by unit tests in GalleryUI.test.tsx β worth a manual look.
- the prop and the ChatContext field channelPaginatorsOrchestrator are renamed to channelManager, and the types ChannelPaginatorsOrchestrator / ChannelPaginatorsOrchestratorState are now ChannelManager / ChannelManagerState. Every useChatContext() consumer reading the old field, and any test that builds a partial ChatContext value, has to be updated. No deprecated prop or type aliases are exported.
- requires stream-chat v10.
- Removed
ChannelStateContextandChannelActionContextalong with
useChannelStateContext(),useChannelActionContext(),
useCreateChannelStateContext()anduseCreateChannelActionContext().
Read the channel viauseChannel()and message state via
useMessagePaginator(); both are backed by LLCStateStores. ChatContextno longer exposes the active channel orsetActiveChannel().
Channel/thread routing is slot-driven β useuseChatViewNavigation()from
stream-chat-react/slot-layout(openChannel,openThread,closeThread).- The
TypingContextprovider path was removed from theChannelruntime.
TypingIndicatornow derives its state from LLC stores and takes
isMessageListScrolledToBottomandscrollToBottomprops. - Context mutation/pagination helpers were replaced by paginator methods:
updateMessage/removeMessage->ingestItem/removeItem;
loadMore/loadMoreNewer->toTail()/toHead();
jumpToMessage/jumpToFirstUnreadMessage->messagePaginator.jumpToMessage()/
jumpToTheFirstUnreadMessage(). Messages are no longer inchannel.state. - Custom send/update/delete/markRead overrides move from ChannelActionContext
callbacks tochannel.configState.requestHandlers/
thread.configState.requestHandlers. ChatViewis a navigation landmark, not a WAI-ARIA Tabs widget: the selector
isrole="navigation"witharia-currenton the active button. There is no
role="tab"/"tablist"/"tabpanel".activeChatViewrenamed toactiveView.ChannelListrenders arole="listbox"whose items arerole="option".- v10 data shapes: channel name/image read from
channel.data.custom.name/
.custom.image; user custom fields fromuser.custom(e.g.username);
attachmentduration/file_size/mime_type/waveform_datafrom
attachment.custom. Message moderation usesmessage.moderation
(moderation_detailsis gone).
Bug Fixes
- adapt to removed stream-chat-js hand-written types (#3274) (ccccce5)
- EmojiPicker: drop @emoji-mart/react peer dependency on v15 (#3300) (e7ce32f)
- MessageList: keep the viewport still when a jumped-to window reaches the live head (#3290) (351d69d), closes #3027
- MessageList: show delivery and read state on every message that has it (#3296) (e74c57a)
- prevent eager requests on Thread component mount (#3282) (6b7dd6c)
- render stable notification host for Channel and Thread (#3291) (ce59094)
Features
- add icons to ComponentContext adapted to stream-chat-react@15 (#3294) (baf4b62)
- adopt stream-chat's branded TimestampNS (#3297) (aaa7fa3), closes GetStream/stream-chat-js#1884 GetStream/stream-chat-js#1884 #3295
- Attachment: render live upload progress on messages being sent (#3295) (4f1459e)
- AudioPlayback: play one voice message at a time, across the whole app (#3287) (68999a7)
- Channel: depend on the Channel instance, not its cid, and narrow its responsibilities (#3286) (088c795)
- context: consolidate message UI overrides on the MessageUI slot (#3268) (0766489), closes #3267
- context: drop componentName param from context consumer hooks (#3267) (9194215)
- i18n: english-only bundle with namespaced, type-checked translation keys (#3261) (17c91bc)
- MessageComposer: let integrators supply the composer MessageComposer edits (#3299) (14e5860)
- migrate components from ChannelActionContext, ChannelStateContext to StateStore instances (#3237) (99cbe67)
- move to new upload API (#3272) (04a31b1)
- move to timestamps in response models (#3278) (cb472c3)
- port the tutorial alignment (#3251) to v15, with panel-aware workspace navigation (#3301) (66557d6)
- rename ChannelPaginatorsOrchestrator to ChannelManager (#3258) (be2c8e1)
- support local unread count on v15 (#3298) (a42c3cf)
- use configuration service API as a single source of truth (#3273) (f66597a)
- use network and WS connection observer services from LLC (#3281) (3098a90)
Refactors
- align Thread with Channel - instance prop, provider, children (#3289) (c2136f3)
- i18n: adopt the shared i18n layer from stream-chat/i18n (#3271) (f68cad3), closes GetStream/stream-chat-js#1830
- i18n: consume the shared runtime from @stream-io/i18n (#3284) (eb3ae7a)
- MessageActions: remove the messageActions prop in from message lists (#3288) (b541c45)