Repository navigation
stream-chat v10.0.0
v10 is a major rewrite of the JavaScript client. The REST layer and types are now generated from the Stream OpenAPI spec. Channel, message, thread and channel-list state lives in reactive state stores and paginators. The connection layer is new, with observable socket and network state and built-in reconnect recovery. Configuration is one declarative tree for the whole client.
v10 is the foundation for stream-chat-react v15 and stream-chat-react-native v10.
Important
This release has many breaking changes. Before you upgrade, read the v9 → v10 migration guide. Every break is listed in the breaking changes reference. The guide is written so an AI coding agent can follow it step by step.
✨ Highlights
Generated API client and types
- All requests and response/request types are generated from the OpenAPI spec. They stay in sync with the backend and replace the hand-written types and API wrappers.
StreamChatmethods take a path object plus a request object. Raw requests and hydrating calls are now separate:client.queryChannels()is the raw request, andqueryChannelsAndHydrate()keeps the v9 behavior. Threads and reactions follow the same*AndHydratepattern.Eventis a discriminated union, so narrowing onevent.typegives you the right payload.- Server timestamps are typed as
TimestampNS(branded unix nanoseconds). A globalDateguard catchesnew Date(timestampNs)mistakes at compile time. - API errors are thrown as
StreamAPIError. Error parsing works even when the app bundles more than one copy of axios.
State stores and paginators
channel.stateis aStateStore. Subscribe to it with selectors instead of reading mutable fields or listening for events.- Messages moved to paginators:
channel.messagePaginator,thread.messagePaginatorandchannel.pinnedMessagesPaginator. They support jumping to a message or to the first unread message, loading in both directions, and pruning old messages from memory. ChannelManagerhas been rewritten. Each client has one, available asclient.channelManager, and it serves any number ofChannelPaginatorchannel lists. It replacesclient.activeChannelswithget(cid),values()andensure({ type, id, data }). Lists are ordered by their sort, events are matched against the full filter, and lists reconcile after a reconnect.- Threads have also been rewritten: the thread list lives in
client.threads.paginator, threads come fromclient.threads.ensure()/get(), andthread.activate()returns its own release function.
Connection and recovery
- Connectivity is published as two state stores:
client.wsConnection.statefor the socket andclient.networkConnection.statefor the device. Both update on every transition. - A new
ConnectionRecoveryManagerreloads the channels and threads you have activated (channel.activate()) after a reconnect. - The client now uses the WebSocket API v2: it connects to
/api/v2/connectand sends auth in the first frame. Long-poll fallback is back. - Reconnects are more robust. The client keeps retrying past the connection timeout, handles a rejected
tokenProviderduring a token refresh, and always firesconnection.recoveredon reconnect, even when state recovery is disabled.
One configuration tree for the client
- Construct the client with
new StreamChat(key, config)/StreamChat.getInstance(key, config), whereconfigis the configuration tree.StreamChatOptionsno longer exists. client.config(theInstanceConfigurationRegistry) holds the configuration for the client, channels, threads and message composers. Register per-instance setup withclient.config.setSetupFunction(key, fn).- Server channel-type flags (
typing_events,read_events,uploads,polls,shared_locations,url_enrichment) are combined with the client configuration using AND, so the server can switch a feature off.
Optimistic updates and offline support
channel.messageOperationshandles optimistic sending, updating and deleting of messages, with offline queueing and retry. Reactions, poll votes and message translations are optimistic too.- Offline DB hydration is more reliable: race conditions are fixed, predefined filters from the DB are respected, and pending tasks fold into queued sends.
Message composer
- New
messageComposer.send()andmessageComposer.update()APIs. - The command middlewares are installed by default, so typing
/giphy catsworks with no setup. - A poll can be sent together with message text and attachments.
attachmentManager.uploadFile(attachment)retries a failed upload in place. Attachments with nothing to upload (custom types,giphy) can be sent and count as sendable content.- Pending uploads settle before the message is sent. Upload notifications include a reference to their composer.
- Link previews are on by default and still follow the channel's
url_enrichmentsetting.
More improvements
- Delivery and read receipts are reported from the furthest read cursor. When local unread tracking is enabled (
channel.readEvents.localUnreadCountEnabled), channels are marked read locally. NotificationManager.ensureTimeout()lets a caller make sure a notification times out even when it can't tell whether the notification was already shown.MessageSearchSourcematches message text by word prefix.- Logging goes through
chatLoggerSystem(@stream-io/logger) and logs to the console by default. - Fewer redundant store publishes and other performance and stability fixes.
⚠️ Breaking changes
These are the most common changes. The breaking changes reference has the full list, including a type rename table.
Install and runtime
- Requires Node
>=22.18.0for installs and builds. Yarn 1 (classic) refuses to install the package on older Node versions, and this applies to React Native and Expo projects too. wsis no longer bundled. The client uses the globalWebSocket, which Node 22+ provides. To use another implementation, pass it in the config:{ client: { wsConnection: { webSocketImpl } } }.StateStoreis no longer exported. Import it from@stream-io/state-store, and make sure the app resolves only one copy of that package.- Server-side features are removed: the API secret, JWT signing, webhook/SQS/SNS helpers and admin-only methods. Use
@stream-io/node-sdkon the server.
Client construction and configuration
- The
secretconstructor argument and theStreamChatOptionsoptions object are removed. Options move underconfig.client, for examplenew StreamChat(key, { client: { baseURL, axiosRequestConfig } }). client.instanceConfigurationServiceis nowclient.config, andsetInstanceConfigurationFunction()is nowclient.config.setSetupFunction().client.configs/client.configsStoreare renamed tochannelServerConfigs/channelServerConfigsStore.channel.getConfig()is removed: usechannel.serverConfigfor the server's settings, orchannel.configfor the resolved configuration.ChannelConfignow means the instance config.client.defaultWSTimeoutand theWebSocketImpl/wsUrlParamsoptions are removed. They are now set in the socket's configuration asconnectTimeoutMs,webSocketImplandurlParams.- The
isLocalUnreadCountEnabledoption is replaced bychannel.readEvents.localUnreadCountEnabled. - Moderation methods moved from
StreamChattoclient.moderation.
Data model and types
- Custom data is stored under
customon every entity, request and event, for examplechannel.data.custom.name,user.custom.*andevent.custom. - Attachment metadata (
mime_type,file_size,duration,waveform_data) moved intoattachment.custom. - Types are generated, so about 385 type names were renamed or removed. Some names, such as
User,Reaction,PollResponseandThreadResponse, are kept but describe a different shape. - Sort parameters are
SortParamRequest[]([{ field, direction }]), and the old sort aliases are removed. EventTypesis renamed toEventType, andEVENT_MAPis removed.- Server timestamps are
TimestampNSnumbers, not strings orDates. CustomCommandDatais removed. Command names are now typed asstring.
Connection and events
connection.changedandtransport.changedare no longer dispatched. Subscribe toclient.wsConnection.state/client.networkConnection.stateinstead.capabilities.changedis no longer dispatched. SelectownCapabilitiesfromchannel.stateinstead.client.wsConnectionis now a wrapper and is nevernull. The live socket isclient.wsConnection.connection.- Requests that watch a channel or subscribe to presence wait for a WebSocket connection id. If no socket is open or opening, they reject instead of returning unwatched data.
connection.recoveredis no longer dispatched when the socket drops again during a recovery.
Channels, messages and threads
client.activeChannels,getChannelById()andgetChannelByMembers()are removed. Useclient.channelManagerandclient.channel()instead.hydrateActiveChannels()is renamed tohydrateChannels().channel.state.messages,last_message_atand pinned messages moved to the paginators listed under Highlights.channel.sendMessage()andsendReaction()are plain requests now. For the optimistic, offline-aware flow, usechannel.messageOperations.channel.markRead()sends the request directly. The v9 reporter-based path is nowmarkReadViaReporter().channel.watch()resolves with the channel instance instead of the server response. It sends no request for a channel that is already watched and loaded, so to refresh, callchannel.reload().channel._disconnect()is renamed tochannel.disconnect().channel.disconnectedis replaced by the read-onlychannel.pendingDisposal.- After a reconnect, only channels you have
activate()d are reloaded. thread.deactivate()is removed. Call the function thatthread.activate()returns.- Results from channel search and message search are no longer watched. Watch the channel when the user opens a result.
- Uploads take
{ file }objects (File,Blobor React Native{ uri, name, type }).sendFileandsendImageare replaced byuploadFileanduploadImage.
Message composer
AttachmentManager.uploadAttachment()is removed. UseuploadFile(attachment).LocalAttachmentMetadataonly requiresid. Afilemust come with itsuploadState.- The command middlewares are installed by default. If you installed them yourself, remove your copies. A UI without command support can remove them by id.
- Server flags now limit the composer config: a flag the server turns off stays off, even if the client enables it. Setters no longer drop writes while the server masks a field.
MessageComposerConfig,AttachmentManagerConfigandLocationComposerConfighave new required fields.applyCommandValidatorOverrideis no longer exported.
i18n
- The translation runtime moved to
@stream-io/i18n, which the React and React Native SDKs share. Streami18n.init()now rejects when i18next fails to initialize.Streami18n.brandis removed.
🐛 Notable fixes
- Pagination and jumping: fixed jumping to thread replies, counting hidden messages around a jump, adjusting offsets when items are removed, issuing the first-page query twice, and windows filled by hydration. Loaded items are now kept when a later page is filtered out.
- Channel lists: fixed reconciliation and merging bugs, edge cases in recovery, and missing updates to
channel.statesubscribers on watcher, ban and member-removal events. - Messages: messages received after a truncate now show up. Fixed retry guards for existing messages, optimistic translated messages, clearing all custom fields on edit, and restoring composer custom data from drafts and edits.
- Threads:
user.updatedis now handled in threads, reloads and queries apply to the registered thread, and the thread cursor is set correctly when a thread is initialized fromsetItems. - Misc:
channel.state.typingnow only trackstyping.startevents. Fixed poll translations frompoll.updated,poll.updatepayloads without an id, and the unread snapshot count. Removed references to theNodeJStype namespace.
📚 Migration resources
Full changelog: v9.55.0...v10.0.0