Skip to content

v10.0.0

Latest

Choose a tag to compare

@stream-ci-bot stream-ci-bot released this 06 Nov 15:49

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.
  • StreamChat methods take a path object plus a request object. Raw requests and hydrating calls are now separate: client.queryChannels() is the raw request, and queryChannelsAndHydrate() keeps the v9 behavior. Threads and reactions follow the same *AndHydrate pattern.
  • Event is a discriminated union, so narrowing on event.type gives you the right payload.
  • Server timestamps are typed as TimestampNS (branded unix nanoseconds). A global Date guard catches new 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.state is a StateStore. Subscribe to it with selectors instead of reading mutable fields or listening for events.
  • Messages moved to paginators: channel.messagePaginator, thread.messagePaginator and channel.pinnedMessagesPaginator. They support jumping to a message or to the first unread message, loading in both directions, and pruning old messages from memory.
  • ChannelManager has been rewritten. Each client has one, available as client.channelManager, and it serves any number of ChannelPaginator channel lists. It replaces client.activeChannels with get(cid), values() and ensure({ 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 from client.threads.ensure() / get(), and thread.activate() returns its own release function.

Connection and recovery

  • Connectivity is published as two state stores: client.wsConnection.state for the socket and client.networkConnection.state for the device. Both update on every transition.
  • A new ConnectionRecoveryManager reloads 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/connect and 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 tokenProvider during a token refresh, and always fires connection.recovered on 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), where config is the configuration tree. StreamChatOptions no longer exists.
  • client.config (the InstanceConfigurationRegistry) holds the configuration for the client, channels, threads and message composers. Register per-instance setup with client.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.messageOperations handles 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() and messageComposer.update() APIs.
  • The command middlewares are installed by default, so typing /giphy cats works 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_enrichment setting.

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.
  • MessageSearchSource matches 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.0 for 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.
  • ws is no longer bundled. The client uses the global WebSocket, which Node 22+ provides. To use another implementation, pass it in the config: { client: { wsConnection: { webSocketImpl } } }.
  • StateStore is 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-sdk on the server.

Client construction and configuration

  • The secret constructor argument and the StreamChatOptions options object are removed. Options move under config.client, for example new StreamChat(key, { client: { baseURL, axiosRequestConfig } }).
  • client.instanceConfigurationService is now client.config, and setInstanceConfigurationFunction() is now client.config.setSetupFunction().
  • client.configs / client.configsStore are renamed to channelServerConfigs / channelServerConfigsStore. channel.getConfig() is removed: use channel.serverConfig for the server's settings, or channel.config for the resolved configuration. ChannelConfig now means the instance config.
  • client.defaultWSTimeout and the WebSocketImpl / wsUrlParams options are removed. They are now set in the socket's configuration as connectTimeoutMs, webSocketImpl and urlParams.
  • The isLocalUnreadCountEnabled option is replaced by channel.readEvents.localUnreadCountEnabled.
  • Moderation methods moved from StreamChat to client.moderation.

Data model and types

  • Custom data is stored under custom on every entity, request and event, for example channel.data.custom.name, user.custom.* and event.custom.
  • Attachment metadata (mime_type, file_size, duration, waveform_data) moved into attachment.custom.
  • Types are generated, so about 385 type names were renamed or removed. Some names, such as User, Reaction, PollResponse and ThreadResponse, are kept but describe a different shape.
  • Sort parameters are SortParamRequest[] ([{ field, direction }]), and the old sort aliases are removed.
  • EventTypes is renamed to EventType, and EVENT_MAP is removed.
  • Server timestamps are TimestampNS numbers, not strings or Dates.
  • CustomCommandData is removed. Command names are now typed as string.

Connection and events

  • connection.changed and transport.changed are no longer dispatched. Subscribe to client.wsConnection.state / client.networkConnection.state instead.
  • capabilities.changed is no longer dispatched. Select ownCapabilities from channel.state instead.
  • client.wsConnection is now a wrapper and is never null. The live socket is client.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.recovered is no longer dispatched when the socket drops again during a recovery.

Channels, messages and threads

  • client.activeChannels, getChannelById() and getChannelByMembers() are removed. Use client.channelManager and client.channel() instead. hydrateActiveChannels() is renamed to hydrateChannels().
  • channel.state.messages, last_message_at and pinned messages moved to the paginators listed under Highlights.
  • channel.sendMessage() and sendReaction() are plain requests now. For the optimistic, offline-aware flow, use channel.messageOperations.
  • channel.markRead() sends the request directly. The v9 reporter-based path is now markReadViaReporter().
  • 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, call channel.reload().
  • channel._disconnect() is renamed to channel.disconnect(). channel.disconnected is replaced by the read-only channel.pendingDisposal.
  • After a reconnect, only channels you have activate()d are reloaded.
  • thread.deactivate() is removed. Call the function that thread.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, Blob or React Native { uri, name, type }). sendFile and sendImage are replaced by uploadFile and uploadImage.

Message composer

  • AttachmentManager.uploadAttachment() is removed. Use uploadFile(attachment).
  • LocalAttachmentMetadata only requires id. A file must come with its uploadState.
  • 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, AttachmentManagerConfig and LocationComposerConfig have new required fields.
  • applyCommandValidatorOverride is 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.brand is 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.state subscribers 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.updated is now handled in threads, reloads and queries apply to the registered thread, and the thread cursor is set correctly when a thread is initialized from setItems.
  • Misc: channel.state.typing now only tracks typing.start events. Fixed poll translations from poll.updated, poll.update payloads without an id, and the unread snapshot count. Removed references to the NodeJS type namespace.

📚 Migration resources

Full changelog: v9.55.0...v10.0.0