Connection status
When the client can't reach Stream, the SDK says why: "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 show the status in your app.
What users get
The user's laptop loses its Wi-Fi on a train:
- The browser reports that the device is offline.
Chatpublishes "Waiting for network…" at once. - The app renders a banner for it, so the user sees "Waiting for network…" at the top of the chat. A screen reader reads "Loading notification: Waiting for network…".
- The Wi-Fi comes back, and so does the connection. The banner goes away.
- The client reloads the channel lists and the open channel. The messages the user missed appear.
Later, the connection to Stream drops while the Wi-Fi 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, the banner shows "Reconnecting…" until it does.
The banner is the app's part. Chat publishes the message but renders nothing for it, so without a banner the user sees no status, and only a screen reader hears it.
How it works across the layers
State: two connection facts and recovery
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. It records a drop the moment it happens.
After a reconnect, client.connectionRecovery reloads the channel lists that have loaded, the active channels and the active threads, then dispatches connection.recovered. Connection explains both stores, the network reporter outside the browser, and recovery.
UI: the lost-connection notification
Chat watches both stores and publishes one system notification while the client can't reach Stream:
- "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.
Both have the type system:network:connection:lost and the severity loading. They stay until the connection is back, then Chat removes them. The hook that does this, useReportLostConnectionSystemNotification, is exported too.
A system notification carries the system tag, so no NotificationList shows it. The app renders it with useSystemNotifications(), anywhere inside Chat. NotificationAnnouncer reads it to screen readers like any other notification. System notifications covers them.
After a reconnect, the components re-render from the stores recovery reloads. Which channels and threads reload depends on what's active:
Channelmarks the channel it shows active, so an open channel always reloads.Threaddoesn't. An open thread reloads only while something holds it active, such asuseActiveThread({ activeThread })fromstream-chat-react/slot-layout, which holds it while the window has focus. Otherwise the thread is marked stale, and loads again the next time it's opened.
For a status UI of your own, useNetworkConnectionState(), useNetworkConnectionStateSelector(selector) and useWSConnectionState() read the two stores.
Layout
The layout isn't involved: the status and the banner are the same wherever channels and threads open.
Turning it on or off
The connection status is always on, for every user, and there's no capability or dashboard setting for it. Chat always publishes the notification. Whether users see it depends on whether the app renders a banner.
What you can change is in client.config:
client.wsConnection.offlineNotificationDisplayDelayMs: how long a socket drop lasts before "Reconnecting…" shows.0shows it on the next tick.client.connectionRecovery.enabled: whether the client reloads lists, channels and threads after a reconnect. With it off,connection.recovereddoesn't fire either.client.networkConnection.statusReporter: what tells the client about the device's network, outside the browser.
client.config.set({
client: { wsConnection: { offlineNotificationDisplayDelayMs: 2000 } },
});The delay is read at each drop, so a change applies from the next one. Configuration explains client.config, and Connection the other settings.
Customizing
Showing the banner
Render the lost-connection notification inside Chat, above the rest of the layout:
import type { PropsWithChildren } from "react";
import type { Notification } from "stream-chat";
import { Chat, useSystemNotifications } from "stream-chat-react";
// Declared outside the component, so the subscription stays the same across renders.
const isConnectionLost = (notification: Notification) =>
notification.type === "system:network:connection:lost";
const ConnectionBanner = () => {
const [lostConnection] = useSystemNotifications({ filter: isConnectionLost });
if (!lostConnection) return null;
return <div className="connection-banner">{lostConnection.message}</div>;
};
export const App = ({ children }: PropsWithChildren) => (
<Chat client={client}>
<ConnectionBanner />
{children}
</Chat>
);The banner has no live region of its own, because NotificationAnnouncer already reads the notification to screen readers. The message is already translated, and says which connection is down. To change the wording, override the translation keys chat.reportLostConnection.waitingNetwork.text and chat.reportLostConnection.reconnecting.text, as Localization explains.
The System notification banner recipe builds a complete banner, with an icon per severity, an animation, and system notifications of your own.
A status of your own
To decide the copy yourself, read the two stores. This component waits before it says "Reconnecting…", as Chat does, so a short drop shows nothing:
import { useEffect, useState } from "react";
import type { NetworkConnectionState } from "stream-chat";
import {
useChatContext,
useNetworkConnectionStateSelector,
useWSConnectionState,
} from "stream-chat-react";
const selectIsOnline = ({ isOnline }: NetworkConnectionState) => ({
isOnline,
});
export const ConnectionStatus = () => {
const { client } = useChatContext();
const { isOnline } = useNetworkConnectionStateSelector(selectIsOnline) ?? {};
const { isHealthy = true } = useWSConnectionState() ?? {};
const [isReconnecting, setIsReconnecting] = useState(false);
useEffect(() => {
if (isHealthy) {
setIsReconnecting(false);
return;
}
const delayMs =
client.wsConnection.config.offlineNotificationDisplayDelayMs;
const timer = setTimeout(() => setIsReconnecting(true), delayMs);
return () => clearTimeout(timer);
}, [client, isHealthy]);
// `=== false`: `undefined` means the network status is unknown, not offline.
if (isOnline === false) return <p>You're offline</p>;
if (isReconnecting) return <p>Reconnecting…</p>;
return null;
};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 the selector at module scope: an inline one resubscribes on every render. Reading state in components explains why.
Refreshing your own data after a reconnect
The client reloads its own lists, channels and threads. Data the app loads itself, such as results of a one-off query, is reloaded on connection.recovered:
import { useEffect, useState } from "react";
import { useChatContext } from "stream-chat-react";
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 <span className="total-unread">{totalUnread}</span>;
};connection.recovered explains when the event fires.
What screen readers hear
NotificationAnnouncer reads the lost-connection notification as "Loading notification: Reconnecting…". To leave it out, or reword it, replace the announcer as Customizing notification announcements shows.
Reference
- Connection:
client.wsConnection,client.networkConnection,client.connectionRecoveryandconnection.recovered. - System notifications: the lost-connection notification and
useSystemNotifications. - Connection hooks:
useNetworkConnectionState,useNetworkConnectionStateSelector,useWSConnectionStateanduseReportLostConnectionSystemNotification. - Notification hooks:
useSystemNotificationsanduseNotificationApi. - System notification banner: a complete banner.
- Configuration tree: the
wsConnection,networkConnectionandconnectionRecoverysettings.