Info:
This is release candidate documentation for Stream Chat React Native SDK v10. For the latest stable version, see the latest version (v9).

How components connect to state

Components in the UI layer don't fetch or keep chat data. They read it from objects in the state layer, and three components hand those objects down the tree: Chat gives the client, Channel gives a channel, and Thread gives a thread. Every component inside reads from the nearest one.

Take these two screens:

import type {
  Channel as StreamChannel,
  Thread as StreamThread,
} from "stream-chat";
import {
  Channel,
  MessageComposer,
  MessageList,
  Thread,
} from "stream-chat-react-native";

export const ChannelScreen = ({ channel }: { channel: StreamChannel }) => (
  <Channel channel={channel}>
    <MessageList />
    <MessageComposer />
  </Channel>
);

export const ThreadScreen = ({ thread }: { thread: StreamThread }) => (
  <Thread thread={thread}>
    <MessageList />
    <MessageComposer />
  </Thread>
);

The two MessageLists are the same component. The first renders channel.messagePaginator, because the nearest owner is the channel. The second renders thread.messagePaginator, with the parent message above the replies, because the nearest owner is the thread. The two MessageComposers work the same way: the first writes into channel.messageComposer, the second into thread.messageComposer. No prop says which one to use.

OverlayProvider and Chat

OverlayProvider goes around your screens, above Chat. It hosts what the SDK draws over everything else: the message menu that opens on a long press, and the image gallery. A screen presented in a separate native window, such as a native modal on iOS, needs its own OverlayProvider; see Chat in modals.

Chat goes around every other SDK component. It takes a connected client, such as the one useCreateChatClient returns (see Quick start).

It provides:

  • the client, through ChatContext, with getAppSettings() for the app's settings and isMessageAIGenerated;
  • translations, through TranslationContext, from the i18nInstance you pass or a default English one;
  • the theme, through ThemeContext, from the style you pass merged over the default theme.

While it's mounted, it also:

  • starts the event subscriptions of client.threads, client.polls and client.reminders, and the reminders' timers, so the thread list, polls and reminders follow events. It stops them when it unmounts. Each ChannelList starts the subscriptions of client.channelManager while it's mounted.
  • reports the device's network state to the client, and closes the WebSocket while the app is in the background and opens it again in the foreground, unless closeConnectionOnBackground is false. See Connection.
  • with enableOfflineSupport, opens the local database for the connected user, and renders ChatLoadingIndicator until it's ready. See Offline support.

Chat doesn't connect the user, load channels or decide which screen shows what.

Channel and Thread

Channel binds one channel to the components inside it. It takes a channel, and configuration props for the message list, the messages and the composer below it:

  • It loads the channel. When it mounts, it watches the channel unless the channel is already watched and loaded with its latest messages, so a channel from a channel list opens without a request. Set initializeOnMount={false} to watch it yourself.
  • It sets the page size of the channel's message list to 25 messages, unless you set one in client.config (see Message paginators).
  • It activates the channel while it's mounted. An active channel stays loaded and is reloaded after a reconnect. See Entity lifecycle.
  • It marks the channel read when it opens with unread messages, unless markReadOnMount is false, and again after a reconnect when the list shows the latest messages.
  • It follows a superseded channel. When the channel you pass is replaced by another object for the same conversation, Channel renders the replacement (see Superseded channels).

Thread binds one thread. It takes a thread, and the same configuration props as Channel:

  • Get the thread from the client, from onThreadSelect on MessageList and ThreadList, or with client.threads.ensure({ channel, parentMessage }). Don't construct one with new Thread(). When the client already holds a thread for that parent message, a second instance stays unmanaged and receives no events.
  • It loads the thread. It loads the first page of replies when none are loaded, and reloads the thread while its state is stale.
  • It activates the thread while it's mounted. An active thread marks new replies read as they arrive, and is reloaded after a reconnect.
  • It doesn't need a Channel around it. Render it on its own screen. Inside a Thread, useChannel() returns the thread's channel.
  • It takes its own configuration. Props set on a Channel don't reach a Thread, so pass the same ones, such as keyboardVerticalOffset, supportedReactions or messageActions, to both.

Which object each part of a channel or thread holds is on What channels and threads hold.

Contexts

Your own components read the same objects through context hooks. This one sits inside a Channel or a Thread:

import { Text } from "react-native";
import {
  useChannel,
  useChatContext,
  useThreadContext,
} from "stream-chat-react-native";

export const WhereAmI = () => {
  const { client } = useChatContext();
  const channel = useChannel();
  const { thread } = useThreadContext();

  return (
    <Text>
      {client.userId} is in {channel.cid}
      {thread ? `, in the thread of message ${thread.id}` : ""}
    </Text>
  );
};
Hook Gives Provided by Outside its provider
useChatContext() client, getAppSettings, isMessageAIGenerated Chat throws
useChannel() the channel: the thread's channel inside a Thread, otherwise the Channel's Channel or Thread throws
useChannelContext() channel and the channel's display options Channel or Thread throws
useThreadContext() { thread, allowThreadMessagesInChannel } Thread returns { thread: undefined }
useMessagePaginator() the message list's paginator: the thread's inside a Thread, otherwise the channel's Channel or Thread throws
useMessageContext() the message, its handlers and display options for one message Message, once for each message a message list renders returns {}
useTranslationContext() t, tDateTimeParser, userLanguage Chat and OverlayProvider throws

useMessageContext() is how a custom message component reads its message. See Overriding components.

The objects these hooks return hold their data in stores. Rendering from a store, and the rules that keep it current, are on Reading state in components.

Pitfalls

  • Wrapping Thread in a Channel. It doesn't need one. Render it on its own screen, or next to Channel on the same screen.
  • Setting props on Channel only. A Thread doesn't inherit them. Pass the same configuration props to both.
  • Reading the channel from your own state inside a thread. Read it with useChannel() and the thread with useThreadContext(), so the component works in both places.
  • Creating a thread with new Thread. Use client.threads.ensure(), so the thread is the one instance the client keeps up to date.

Used in: every page that renders state, including Channel UI, Thread UI and Message list UI.