Connection status
When the client can't reach Stream, the SDK says so: "Waiting for network..." when the device is offline, "Reconnecting..." when the network is fine but the connection to Stream dropped. When the connection comes back, the client brings what's on screen up to date by itself. This page covers what users get, how the parts fit together, and how to change the status the app shows.
What users get
The user's phone loses its signal on a train:
- The device reports that it's offline. The open channel's message list shows "Waiting for network..." in a bar at its top, and the channel list shows "Reconnecting...".
- With accessibility turned on, a screen reader says "Offline".
- The signal comes back, and so does the connection. The bars go away, and the screen reader says "Connected".
- The client reloads the channel lists and the open channel. The messages the user missed appear.
Later, the connection to Stream drops while the network works, because the server closed it or a health check timed out:
- If it comes back within five seconds, the user sees nothing. Most drops recover in under a second.
- If it doesn't, both lists show "Reconnecting..." until it does, and the screen reader says "Reconnecting".
The channel list shows no bar while it loads its first page. When a load fails after the first page has loaded and the connection is fine, it shows "Error while loading, please reload/refresh" in the same place, and tapping it loads the list again.
How it works across the layers
- State. The client keeps two separate facts, because they disagree in both directions.
client.networkConnection.stateholdsisOnline, whether the device has a network. It'sundefinedwhile unknown, so test it with=== false.client.wsConnection.stateholdsisHealthy, whether the WebSocket to Stream is up.ChatinstallsnetInfoStatusReporter, which reports the device's network from@react-native-community/netinfo. After a reconnect,client.connectionRecoveryreloads the channel lists that have loaded, the active channels and the active threads, and dispatchesconnection.recoveredonce the channels and threads have reloaded. Connection explains both stores, the reporter and recovery. - UI.
MessageListandMessageFlashListrenderNetworkDownIndicatorat their top, in a channel and in a thread.ChannelListrendersChannelListHeaderNetworkDownIndicatorat its top.ChannelandThreadeach render aNotificationAnnouncer, which tells screen readers when the connection changes. All three read the two stores through the SDK's hooks. - Navigation. Not involved. The bars and the announcements are the same on every screen.
Chat also closes the socket when the app goes to the background, and opens it again when the app comes back. So every return to the foreground is a reconnect, and recovery runs each time. Closing the socket in the background explains it.
After a reconnect, the components re-render from the stores recovery reloads. Channel marks the channel it shows active, and Thread marks its thread active, so an open channel or thread always reloads.
When a bar shows
NetworkDownIndicator decides by itself whether to render:
- "Waiting for network..." as soon as the device reports it's offline;
- "Reconnecting..." when the socket has been down for
client.wsConnection.config.offlineNotificationDisplayDelayMs, five seconds by default, while the network is up or unknown; - nothing otherwise.
ChannelListHeaderNetworkDownIndicator only renders its text. ChannelList renders it in the same two cases, and the text is "Reconnecting..." in both.
The socket's health with the delay applied comes from useSettledWSConnectionHealth(). It returns false once a drop has lasted the delay, or at once when the device reports it's offline, and true as soon as the socket is back. A first connection that hasn't finished reads as healthy until the same delay has passed.
Turning it on or off
The connection status is always on, for every user, and there's no capability or dashboard setting for it. What you can change:
client.wsConnection.offlineNotificationDisplayDelayMsinclient.config: how long a socket drop lasts before "Reconnecting..." shows.0shows it at once. The hooks read it from the client's configuration, so a change applies at once.client.connectionRecovery.enabledinclient.config: whether the client reloads lists, channels and threads after a reconnect. With it off,connection.recoveredisn't dispatched either.announceConnectionStatein theaccessibilityprop ofOverlayProvider: whether screen readers hear the changes. It's on by default once accessibility is on.closeConnectionOnBackgroundonChat: whether the socket closes in the background.
client.config.set({
client: { wsConnection: { offlineNotificationDisplayDelayMs: 2000 } },
});Configuration explains client.config, and Connection the other settings.
To show no bar at all, replace both indicators with components that render nothing, as below.
Customizing
Replacing the indicators
NetworkDownIndicator and ChannelListHeaderNetworkDownIndicator are override keys. Neither receives props. MessageList always renders NetworkDownIndicator, so a replacement decides itself whether to show and where: the default sits absolutely positioned at the top of the list. ChannelList renders its indicator only while the connection is down, in a container at the top of the list.
This replacement shows its own copy in the message list, and nothing in the channel list:
import type { PropsWithChildren } from "react";
import { StyleSheet, Text, View } from "react-native";
import type { NetworkConnectionState } from "stream-chat";
import {
useNetworkConnectionStateSelector,
useSettledWSConnectionHealth,
WithComponents,
} from "stream-chat-react-native";
import type { ComponentOverrides } from "stream-chat-react-native";
// Declared at module scope, so the subscription stays the same across renders.
const selectIsOnline = ({ isOnline }: NetworkConnectionState) => ({ isOnline });
const ConnectionBar = () => {
const { isOnline } = useNetworkConnectionStateSelector(selectIsOnline) ?? {};
const isConnected = useSettledWSConnectionHealth();
// `=== false`: `undefined` means the network status is unknown, not offline.
if (isOnline !== false && isConnected) return null;
return (
<View style={styles.bar}>
<Text style={styles.text}>
{isOnline === false ? "You're offline" : "Connecting to chat..."}
</Text>
</View>
);
};
const overrides: ComponentOverrides = {
ChannelListHeaderNetworkDownIndicator: () => null,
NetworkDownIndicator: ConnectionBar,
};
export const WithConnectionBar = ({ children }: PropsWithChildren) => (
<WithComponents overrides={overrides}>{children}</WithComponents>
);
const styles = StyleSheet.create({
bar: {
backgroundColor: "#fff3cd",
left: 0,
padding: 8,
position: "absolute",
right: 0,
top: 0,
},
text: { color: "#664d03", textAlign: "center" },
});The copy is plain English here. Pass it through t() with keys of your own to translate it, as Localization explains.
A status of your own
The same hooks work anywhere inside Chat, for example in a screen header:
useNetworkConnectionState()anduseNetworkConnectionStateSelector(selector)readclient.networkConnection.state;useWSConnectionState()anduseWSConnectionStateSelector(selector)readclient.wsConnection.stateas it is, with every drop the moment it happens;useSettledWSConnectionHealth()returns the socket's health with the delay applied, as described in When a bar shows.
There's no hook that combines the two facts. Whether "no network and no socket" reads as offline or as reconnecting is a choice for your copy. Declare a selector at module scope, because an inline one resubscribes on every render. Reading state in components explains why.
Styling and texts
The theme styles the default bars: messageList.errorNotification and messageList.errorNotificationText for NetworkDownIndicator, and channelListHeaderErrorIndicator.container and channelListHeaderErrorIndicator.errorText for ChannelListHeaderNetworkDownIndicator. See Theming.
The texts are the translation keys common.waitingForNetwork.text and common.reconnecting.text. Override them as Localization explains.
Refreshing your own data after a reconnect
The client reloads its own lists, channels and threads. Data the app loads itself, such as the result of a one-off query, is loaded again on connection.recovered:
import { useEffect, useState } from "react";
import { Text } from "react-native";
import { useChatContext } from "stream-chat-react-native";
export const TotalUnreadBadge = () => {
const { client } = useChatContext();
const [totalUnread, setTotalUnread] = useState(0);
useEffect(() => {
const load = async () => {
const { total_unread_count } = await client.unreadCounts();
setTotalUnread(total_unread_count);
};
void load();
const { unsubscribe } = client.on("connection.recovered", () => {
void load();
});
return unsubscribe;
}, [client]);
return <Text>{totalUnread}</Text>;
};connection.recovered explains when the event fires.
What screen readers hear
With accessibility on, the NotificationAnnouncer in each Channel and Thread announces each change of the socket's settled health:
- "Offline" when it goes down while the device reports it's offline;
- "Reconnecting" when it goes down with the network up or unknown;
- "Connected" when it comes back.
It doesn't announce the state it finds when it mounts, only the changes after that. The texts are the translation keys a11y.connection.offline.accessibilityLabel, a11y.connection.reconnecting.accessibilityLabel and a11y.connection.connected.accessibilityLabel. To turn the announcements off, pass announceConnectionState: false:
import type { PropsWithChildren } from "react";
import { OverlayProvider } from "stream-chat-react-native";
// Declared at module scope, so the provider gets the same object on every render.
const accessibility = { announceConnectionState: false, enabled: true };
export const AppOverlayProvider = ({ children }: PropsWithChildren) => (
<OverlayProvider accessibility={accessibility}>{children}</OverlayProvider>
);The bars themselves aren't live regions, so the announcer is the only thing a screen reader hears about the connection.
Reference
- Connection:
client.wsConnection,client.networkConnection,netInfoStatusReporter,client.connectionRecoveryandconnection.recovered. - Connection hooks:
useNetworkConnectionState,useNetworkConnectionStateSelector,useWSConnectionState,useWSConnectionStateSelectoranduseSettledWSConnectionHealth. - Indicators:
NetworkDownIndicatorandChannelListHeaderNetworkDownIndicator. - ComponentsContext: the
NetworkDownIndicator,ChannelListHeaderNetworkDownIndicatorandChannelListHeaderErrorIndicatoroverride keys. - Chat: the
closeConnectionOnBackgroundprop. - OverlayProvider: the
accessibilityprop. - Configuration tree: the
wsConnection,networkConnectionandconnectionRecoverysettings.