# Connection status

> **Info:** This is release candidate documentation for **Stream Chat React Native SDK v10**. For the latest stable version, see the [latest version (v9)](https://getstream.io/chat/docs/sdk/react-native/).

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:

1. 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...".
2. With [accessibility](https://getstream.io/chat/docs/sdk/react-native/v10/features/app-experience/accessibility/) turned on, a screen reader says "Offline".
3. The signal comes back, and so does the connection. The bars go away, and the screen reader says "Connected".
4. 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.state` holds `isOnline`, whether the device has a network. It's `undefined` while unknown, so test it with `=== false`. `client.wsConnection.state` holds `isHealthy`, whether the WebSocket to Stream is up. `Chat` installs `netInfoStatusReporter`, which reports the device's network from `@react-native-community/netinfo`. After a reconnect, `client.connectionRecovery` reloads the channel lists that have loaded, the active channels and the active threads, and dispatches `connection.recovered` once the channels and threads have reloaded. [Connection](https://getstream.io/chat/docs/sdk/react-native/v10/state/connection/) explains both stores, the reporter and recovery.
- **UI.** `MessageList` and `MessageFlashList` render `NetworkDownIndicator` at their top, in a channel and in a thread. `ChannelList` renders `ChannelListHeaderNetworkDownIndicator` at its top. `Channel` and `Thread` each render a `NotificationAnnouncer`, 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](https://getstream.io/chat/docs/sdk/react-native/v10/state/connection/#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.offlineNotificationDisplayDelayMs` in `client.config`: how long a socket drop lasts before "Reconnecting..." shows. `0` shows it at once. The hooks read it from the client's configuration, so a change applies at once.
- `client.connectionRecovery.enabled` in `client.config`: whether the client reloads lists, channels and threads after a reconnect. With it off, `connection.recovered` isn't dispatched either.
- `announceConnectionState` in the `accessibility` prop of `OverlayProvider`: whether screen readers hear the changes. It's on by default once accessibility is on.
- `closeConnectionOnBackground` on `Chat`: whether the socket closes in the background.

```ts
client.config.set({
  client: { wsConnection: { offlineNotificationDisplayDelayMs: 2000 } },
});
```

[Configuration](https://getstream.io/chat/docs/sdk/react-native/v10/state/state-and-entities/configuration/) explains `client.config`, and [Connection](https://getstream.io/chat/docs/sdk/react-native/v10/state/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:

```tsx
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](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/localization/) explains.

### A status of your own

The same hooks work anywhere inside `Chat`, for example in a screen header:

- `useNetworkConnectionState()` and `useNetworkConnectionStateSelector(selector)` read `client.networkConnection.state`;
- `useWSConnectionState()` and `useWSConnectionStateSelector(selector)` read `client.wsConnection.state` as 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](#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](https://getstream.io/chat/docs/sdk/react-native/v10/ui/reading-state-in-components/#selector-identity) 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](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/theming/).

The texts are the translation keys `common.waitingForNetwork.text` and `common.reconnecting.text`. Override them as [Localization](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/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`:

```tsx
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](https://getstream.io/chat/docs/sdk/react-native/v10/state/connection/#connectionrecovered) 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`:

```tsx
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](https://getstream.io/chat/docs/sdk/react-native/v10/state/connection/): `client.wsConnection`, `client.networkConnection`, `netInfoStatusReporter`, `client.connectionRecovery` and `connection.recovered`.
- [Connection hooks](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/connection-hooks/): `useNetworkConnectionState`, `useNetworkConnectionStateSelector`, `useWSConnectionState`, `useWSConnectionStateSelector` and `useSettledWSConnectionHealth`.
- [Indicators](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/utilities/indicators/): `NetworkDownIndicator` and `ChannelListHeaderNetworkDownIndicator`.
- [ComponentsContext](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/channel/componentscontext/): the `NetworkDownIndicator`, `ChannelListHeaderNetworkDownIndicator` and `ChannelListHeaderErrorIndicator` override keys.
- [Chat](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/chat/chat/): the `closeConnectionOnBackground` prop.
- [OverlayProvider](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/chat/overlayprovider/): the `accessibility` prop.
- [Configuration tree](https://getstream.io/chat/docs/sdk/react-native/v10/reference/state/configuration-tree/): the `wsConnection`, `networkConnection` and `connectionRecovery` settings.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/v10/features/app-experience/connection-status/](https://getstream.io/chat/docs/sdk/react-native/v10/features/app-experience/connection-status/).