Overview
Stream Chat React is a React SDK for chat on the Stream Chat API: team and social messaging, livestream chat, customer support and similar apps.
It has two layers:
- State lives in
stream-chat, the JavaScript client. Channels, threads, message lists and composers are objects that keep their data in reactive stores. You configure them throughclient.config. - UI lives in
stream-chat-react. Its components read those stores and re-render when they change. You replace any of them throughWithComponents. Its optional layout part,stream-chat-react/slot-layoutwithChatView, decides where a channel or a thread opens.
To see the two layers work together, read How the SDK is built. It follows one message from the composer to the message list.
The SDK components cover:
- Channel lists, message lists and the message composer
- Threads, the thread list and quoted replies
- Reactions, mentions and slash commands
- Image, video and file attachments, with uploads
- Voice recordings and audio playback
- Polls and location sharing
- Read state, unread counts and typing indicators
- Search across channels, messages and users
Start here
Build a working chat app from an empty project.
Install the SDK and render your first chat components.
Follow one message through the state and UI layers.
The stream-chat client API for connections, channels and messages.
Build with Stream CLI and Agent Skills
The Stream CLI and Agent Skills provide AI coding agents, such as Claude Code, Cursor, and Codex, with the tools and knowledge necessary to build apps with Stream.
Install the CLI and add the skills to your project:
curl -fsSL https://getstream.io/cli.sh | bash
getstream skills stream-reactOnce installed, invoke it from your agent:
/stream Add Stream Chat to my React app.The /stream skill acts as a router, reading your request and dispatching it to the specialist skill. You can also invoke the /stream-react skill directly.
Stream Agent Skills can also be installed from skills.sh.
Best practices
- Start with the SDK's components and replace only the parts that need a different design.
- Use built-in features (threads, reactions, uploads, indicators) before building your own.
- Change behaviour through
client.config, and appearance throughWithComponentsand CSS variables. Both stay in place when you update the SDK. - Check that your target browsers are on the supported list below.
What's new in v15
v15 is an architectural release. The UI is largely unchanged; what moved is where state lives, how a channel gets on screen, and how the SDK is configured.
State lives in the low-level client
Messages, channel lists, threads, search and the composer are owned by stream-chat objects, each holding
a StateStore. Components subscribe to the slice they render with useStateStore, so there is one source
for any value and no copy kept in React.
For example, this badge shows the number of unread threads and updates when it changes:
import type { ThreadManagerState } from "stream-chat";
import { useChatContext, useStateStore } from "stream-chat-react";
// Declared outside the component, so the subscription isn't recreated on every render.
const selector = (state: ThreadManagerState) => ({
unreadThreadCount: state.unreadThreadCount,
});
export const UnreadThreadsBadge = () => {
const { client } = useChatContext();
const { unreadThreadCount } = useStateStore(client.threads.state, selector);
return <span>{unreadThreadCount} unread threads</span>;
};ChannelStateContext and ChannelActionContext are gone with it. State is read from the instance, and
actions are called on it. See Reactive state and
Reading state in components.
Pagination is a shared model
One BasePaginator backs channel lists, message history, thread replies, reminders and user groups, with
cursors, ordering and loading state in one place. Let's take channel pagination as an example. ChannelList takes a ChannelPaginator as its data
source, and client.channelManager keeps every registered channel paginator in sync from a single set of websocket
subscriptions, so several lists can coexist. For channel lists, see
ChannelManager and ChannelPaginator.
Layout is a plugin, and several channels can be active
Several Channel components can be mounted at once, each bound to its own channel by prop. Each one
activates its channel with Channel.activate(), so the client knows which channels are in use: it keeps
them loaded and reloads them after a reconnect. See
Entity lifecycle. There is no
single selected channel in the chat context. Which channel is on screen is a layout question, answered by the
slot layout plugin, which ships as its own entry
point. Core components navigate through
workspace navigation, an interface that
is inert when no layout plugin is installed. An app can use the plugin, or implement the interface over its
own layout.
One place to configure instances
client.config is a configuration registry for the objects the SDK builds: channels, threads, composers, paginators and
the client's own managers. It replaces the per-component request-handler props, and it is where a setup
function goes when configuration values cannot express the customization. See
Configuration.
Localization by stable keys
Translation keys are namespaced identifiers rather than English sentences, with the copy inline at each call site. English is the only bundled language, and a partial custom dictionary is safe because an unsupplied key still renders. See translations.
Upgrading from v14 is covered in the v15 migration guide.
Architecture
The two layers are two packages, and they meet in stores.
State: stream-chat. The client creates one Channel object per channel and one Thread object per thread. Each keeps its data in stores. For a channel, channel.state holds the members, read state and typing users, channel.messagePaginator holds the loaded messages, and channel.messageComposer holds what the user is writing. WebSocket events update these stores. You configure these objects through client.config. None of this depends on React.
UI: stream-chat-react. A component subscribes to the part of a store it shows, with useStateStore, and re-renders when that part changes. Chat gives every component below it the client, read with useChatContext(). Channel gives its children a channel and Thread a thread, read with useChannel() and useThreadContext(). You replace any component through WithComponents.
Layout: stream-chat-react/slot-layout. This part of the UI is optional. SDK components open channels and threads through useWorkspaceNavigation(), and ChatView decides where each one appears. Without a layout, opening does nothing, and the app renders Channel and Thread where it wants them.
For example, when a new message arrives, channel.messagePaginator adds it to its store. MessageList subscribes to that store, so it re-renders with the new message. React keeps no copy of the messages.
Supported platforms
Our React Chat SDK can run on all modern browsers and platforms released after April 2024. Here is the list of supported platforms:
- Chrome 124+
- Firefox 124+
- Edge 124+
- Safari and Mobile Safari 17.4+
Although the SDK works on other exotic and older browser versions, these are the ones we officially support and test on, so we recommend using them for the best experience.
What's next
Add the SDK packages and stylesheets to your project.
How the stores hold state and notify subscribers.
Style the components with CSS variables.
Replace SDK components with your own, step by step.
FAQ
How do I upgrade from v14?
Follow the v15 upgrade guide.
Can I show in-app notifications for chat events?
Yes. The notifications guide covers useNotificationApi and the NotificationList component.
Does chat include moderation?
Yes. AI engines screen messages and users can flag content into a dashboard review queue. See moderation for chat.