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

State and entity hooks

These hooks give a component the client, the channel or the thread it renders in, and a way to read any store. They're exported from stream-chat-react-native and stream-chat-expo.

How to use them, the useStateStore rules and the pitfalls are on Reading state in components. What a store is, and which objects have one, is on Reactive state.

Each entry gives the signature, then:

  • Returns: what the hook gives back.
  • Reads: the store or the context it reads.
  • Re-renders: what makes the component render again.
  • Needs: where the component must sit, and what the hook does outside that place.

useStateStore

Subscribes to one store and returns the part of its value that a selector picks.

import type { StateStore } from "@stream-io/state-store";

type Selection = Readonly<Record<string, unknown> | Readonly<unknown[]>>;

declare function useStateStore<
  T extends Record<string, unknown>,
  O extends Selection,
>(store: StateStore<T>, selector: (state: T) => O): O;

declare function useStateStore<
  T extends Record<string, unknown>,
  O extends Selection,
>(store: StateStore<T> | undefined, selector: (state: T) => O): O | undefined;
  • Returns: what the selector returns, an object or a tuple. undefined when store is undefined, so a component can pass thread?.state.
  • Reads: the store you pass. StateStore is the store type from @stream-io/state-store.
  • Re-renders: when a value in the selection changes. The hook compares each key of the previous selection with ===. A different store or selector makes it subscribe again, so define selectors at module scope.
  • Needs: nothing. It works outside Chat.

The unread thread count has no hook of its own. Read it from client.threads.state:

import { Text } from "react-native";
import type { ThreadManagerState } from "stream-chat";
import { useChatContext, useStateStore } from "stream-chat-react-native";

const unreadSelector = (state: ThreadManagerState) => ({
  unreadThreadCount: state.unreadThreadCount,
});

export const UnreadThreadsBadge = () => {
  const { client } = useChatContext();
  const { unreadThreadCount } = useStateStore(
    client.threads.state,
    unreadSelector,
  );

  if (!unreadThreadCount) return null;
  return <Text>{unreadThreadCount}</Text>;
};

useChatContext

Returns what Chat provides.

useChatContext(): ChatContextValue
  • Returns:
    • client: the client passed to Chat.
    • getAppSettings(): resolves to the app settings. The first call for a user requests them, and later calls reuse the result. A failed request isn't kept, so the next call requests again. With offline support, a successful request is stored in the offline database, and a failed one falls back to the stored copy. Chat makes the first call itself once a user is connected.
    • isMessageAIGenerated: the Chat prop of the same name. It returns false for every message by default.
  • Reads: React context. It subscribes to no store, so reading a getter such as client.user from it doesn't re-render the component when the value changes.
  • Re-renders: when Chat gets another client or another isMessageAIGenerated function. getAppSettings keeps its identity. Pass isMessageAIGenerated from module scope, or every render of Chat re-renders every component that reads the context.
  • Needs: Chat. Throws outside it.

The context is on ChatContext.

useCreateChatClient

Creates a client and connects a user.

useCreateChatClient(params: {
  apiKey: string;
  tokenOrProvider: TokenOrProvider;
  userData: ClientUser;
  options?: DeepPartial<InstanceConfigTree>;
}): StreamChat | null

The params:

  • apiKey: your Stream API key.

  • tokenOrProvider: the user's token, or a function that returns one.

  • userData: the user to connect, as client.connectUser() takes it. Only id is required.

  • options: the configuration tree, the same one client.config.set() takes. The hook reads it on the first render only. Unless it sets client.networkConnection.statusReporter itself, the hook adds netInfoStatusReporter, so the network status is known from the start. Chat keeps a reporter set here.

  • Returns: null until client.connectUser() resolves, and then the client. Each client is a new StreamChat, not the shared instance of StreamChat.getInstance().

  • Reads: no store and no context.

  • Re-renders: when the client connects, and when it's replaced. A new apiKey, a new tokenOrProvider or a userData with another id disconnects the user, returns null, and connects a new client. Other changes to userData are ignored. Define a token provider at module scope or with useCallback, or every render creates a new client.

  • Needs: nothing. Call it above Chat, and pass the client to Chat once it isn't null.

When the component unmounts, the hook waits for the connection to finish, then disconnects the user. If client.connectUser() rejects, for example on a cold start without a network, the hook keeps returning null. To open the app on locally stored data with offline support, create the client yourself, as Offline support shows. Quick start uses the hook.

useChannel

Returns the channel the component renders in.

useChannel(): Channel
  • Returns: inside a Thread, the thread's channel. Otherwise the channel of the nearest Channel.
  • Reads: the Thread and Channel contexts. No store.
  • Re-renders: when the Thread or the Channel above switches to another thread or channel, or the channel is frozen or unfrozen. Other changes to the channel's state don't re-render it: read them with useStateStore(channel.state, selector).
  • Needs: a Channel or a Thread. Throws outside both.

Inside a Thread, channel.messagePaginator and channel.messageComposer are the channel's, not the thread's. The right owner inside threads says which to read instead. The channel's state is on What channels and threads hold.

useChannelContext

Returns the channel and the display settings of the nearest Channel or Thread.

useChannelContext(): ChannelContextValue
  • Returns:
    • channel: the channel. Inside a Thread, the thread's channel.
    • disabled: whether the channel is frozen.
    • enforceUniqueReaction, enableMessageGroupingByUser, maxTimeBetweenGroupedMessages, hideDateSeparators, hideStickyDateHeader and allowDateSeparatorForSystemMessages: the props of the same names on Channel or Thread.
  • Reads: the context Channel and Thread provide. No store.
  • Re-renders: when the channel instance changes, or the channel is frozen or unfrozen. A change to one of the display props after mount doesn't provide a new value.
  • Needs: a Channel or a Thread. Throws outside both.

useChannel() returns the same channel. Use useChannelContext() when you also need the settings. The context is on ChannelContext.

useThreadContext

Returns the thread the component renders in.

useThreadContext(): ThreadContextValue
  • Returns: { thread, allowThreadMessagesInChannel }. thread is the Thread's thread, and allowThreadMessagesInChannel its prop of that name, true by default. Outside a Thread, { thread: undefined, allowThreadMessagesInChannel: false }.
  • Reads: the context Thread provides. No store.
  • Re-renders: when the Thread above switches to another thread, or its allowThreadMessagesInChannel prop changes.
  • Needs: nothing. Never throws, so a component can tell whether it renders in a thread.

The SDK has no hook for a thread's state. Read thread.state with useStateStore. Its fields are on What channels and threads hold and Thread and thread manager.

import { Text } from "react-native";
import type { ThreadState } from "stream-chat";
import { useStateStore, useThreadContext } from "stream-chat-react-native";

const replyCountSelector = (state: ThreadState) => ({
  replyCount: state.replyCount,
});

export const ReplyCount = () => {
  const { thread } = useThreadContext();
  const { replyCount } = useStateStore(thread?.state, replyCountSelector) ?? {};

  if (replyCount === undefined) return null;
  return <Text>{replyCount} replies</Text>;
};

useThreadListItemContext

Returns what the ThreadList row being rendered shows.

useThreadListItemContext(): ThreadListItemContextValue
  • Returns:
    • thread: the row's thread, and channel, its channel.
    • parentMessage: the thread's parent message.
    • lastReply: the newest reply, from thread.messagePaginator.aggregateState.
    • ownUnreadMessageCount: the current user's unread replies.
    • draftMessage: what the user has typed in the thread's composer, or undefined when it's empty.
    • dateString: the time of the newest reply, formatted with the timestamp.ThreadListItem translation key. deletedAtDateString: the time the thread was deleted, formatted the same way, or undefined.
  • Reads: the context ThreadListItem sets for its row. ThreadListItem subscribes to thread.state, the paginator's aggregateState and the composer's stores, and builds the value from them.
  • Re-renders: whenever ThreadListItem renders, since it passes a new value each time. It renders when one of those stores changes.
  • Needs: a ThreadListItem, which ThreadList renders for each thread. Outside one it returns an empty object, typed as the full value, so check thread before you use it.

Use it in a component you pass to WithComponents as ThreadListItem, as Replies and threads shows.

useMutedUsers

Returns the users the current user has muted.

useMutedUsers(): UserMuteResponse[]
  • Returns: the user mutes, each with the muted user as target. [] before they load.
  • Reads: client.mutedUsersStore, field mutedUsers. The client fills it when the user connects, and replaces it on notification.mutes_updated.
  • Re-renders: when the list of mutes changes.
  • Needs: Chat. Throws outside it.

useMutedChannels

Returns the channels the current user has muted.

useMutedChannels(): ChannelMute[]
  • Returns: client.mutedChannels, the channel mutes.
  • Reads: client.mutedChannels, a plain field rather than a store. The hook reads it again when the health.check event or the notification.channel_mutes_updated event arrives. The client also replaces it on connection.ok, which the hook doesn't listen for, so the mutes of a new connection show on the next health.check.
  • Re-renders: when one of those events replaces the list.
  • Needs: Chat. Throws outside it.

Whether one channel is muted is also in its own state: useIsChannelMuted(channel) on Channel preview hooks reads it.

Stores without a hook

Read these stores with useStateStore and your own selector:

To read Store and field
Blocked user IDs client.blockedUsers, userIds
The unread thread count, unseen threads client.threads.state, unreadThreadCount and unseenThreadIds
A channel's settings in effect channel.configState
A composer's settings in effect messageComposer.configState
A thread's state thread.state, with the thread from useThreadContext()
A poll's state poll.state, with the poll from usePollContext()

The settings stores are explained on Configuration. The connected user, client.user, has no store: read it when you need it, or listen for the events that change it.

Other exported hooks

Context hooks of components

Each component that provides a context exports a hook for it. The values are listed with the component:

Hook Provided by Reference
useOverlayContext OverlayProvider OverlayContext
useComponentsContext WithComponents, and the defaults ComponentsContext
useOwnCapabilitiesContext Channel and Thread OwnCapabilitiesContext
useMessagesContext Channel and Thread MessagesContext
useMessageContext Message MessageContext
useMessageInputContext Channel and Thread MessageInputContext
useChannelsContext ChannelList ChannelsContext
useThreadsContext ThreadList ThreadList
usePollContext Poll Poll components
useChannelDetailsContext and the context hooks of its screens ChannelDetails Channel details contexts
useImageGalleryContext OverlayProvider Image gallery
useAttachmentPickerContext Channel and Thread Attachment picker
useTheme OverlayProvider and Chat Theming
useTranslationContext Chat and OverlayProvider Localization

The hooks of the composer, the message, polls, notifications and channel details are on their own pages in this section.

Hooks that belong to a feature

  • Audio playback: useAudioPlayer, useActiveAudioPlayer and useAudioPlayerContext. See Voice recordings and audio playback.
  • The message menu: useHasActiveId, which returns true while the menu of a message is open. See OverlayContext.
  • Bottom sheets: useBottomSheetContext, which the content of a BottomSheetModal calls to close() the sheet with its animation, or to dismiss() it before another native modal opens. See Overlays and bottom sheets.
  • The image gallery: useImageGalleryVideoPlayer. See Image gallery.
  • Accessibility: useAccessibilityContext, useA11yLabel, useAccessibilityActivateAction, useAccessibilityAnnouncer, useAccessibilityServiceEnabled, useAnnounceOnShow, useAnnounceOnStateChange, useIncomingMessageAnnouncements, useReducedMotionPreference, useResolvedModalAccessibilityProps, useScreenReaderEnabled, useScreenReaderMountFocus and useSetAccessibilityFocus. See Accessibility.
  • The keyboard: useKeyboardContext and useAfterKeyboardOpenCallback. See Keyboard and insets.
  • Translations: useStreami18n, which Chat calls to start a Streami18n instance. See Localization.

Utilities

These hooks don't read chat state. The SDK's components use them, and they're exported for components of your own:

Hook What it does
useStableCallback(callback) Returns a function that keeps its identity and always calls the latest callback.
useViewport(rounded?) Returns { vh, vw }, which convert a percentage of the window's height or width to points. Updates when the window resizes.
useScreenDimensions(rounded?) The same as useViewport, measured against the screen instead of the window. It's deprecated, because the two differ in split view and on foldables. Use useViewport.
useScreenOrientation() Returns 'landscape' while the window is wider than it is tall, 'portrait' otherwise.
useAppStateListener(onForeground?, onBackground?) Calls onBackground when the app goes to the background, and onForeground when it becomes active again. The inactive state calls neither.
useIsSvg(uri) Returns whether uri points to an SVG, by its .svg extension or an image/svg+xml data URI.
useLoadingImage() Returns { isLoadingImage, isLoadingImageError, setLoadingImage, setLoadingImageError, onReloadImage } for an image component, and reloads a failed image when the device comes back online. Needs Chat.

Hooks left out of this reference

The rest of the exported hooks are the SDK's own plumbing. They're exported so the default components can be rebuilt from parts, but they change with the components that use them:

  • The useCreate*Context hooks (useCreateChatContext, useCreateChannelContext, useCreateMessagesContext, useCreateMessageContext, useCreateThreadContext, useCreateMessageInputContext, useCreateInputMessageInputContext) build the values the providers pass down. Read the contexts with their use*Context hooks instead.
  • The message overlay and portal hooks (useOverlayController, useIsOverlayActive, useIsOverlayClosing, useIsTopOverlayHostLayer, useShouldTeleportToClosingPortal, useClosingPortalHostBlacklist, useClosingPortalHostBlacklistState, useClosingPortalLayouts, usePortalSettledCallback, useWithPortalKeyboardSafety, useMessageOverlayRuntimeContext, useMessageOverlayTargetContext) drive the message context menu. Overlays and bottom sheets explains what you can change there.
  • useRAFCoalescedValue, useMessageList, useMicPositionContext and useChatConfigContext serve the message list, the voice recorder and image resizing.
  • useInAppNotificationsState is deprecated. It reads a store of its own, which no SDK component shows. Use the notification hooks on Notification hooks, which read client.notifications. openInAppNotification(notification) becomes addNotification({ message, origin, options }) from useNotificationApi(), closeInAppNotification(id) becomes removeNotification(id), and notifications becomes useNotifications(). NotificationList can replace a list of your own that rendered them, as Displaying notifications shows.