# Members and online status

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

This recipe renders the members of the open channel with their online status, and keeps the list current as members come online, go offline, join or leave.

## Best practices

- Read members from `channel.state` with `useChannelMembersState(channel)`. It subscribes to the store, so the component re-renders when the members change.
- Watch the channel with presence, so the server sends presence changes. Without it, `online` shows the value from when the channel loaded.
- Don't add your own `user.presence.changed` listener. The client already writes each presence change into the members it holds.
- Keep the list simple. With presence on, it re-renders every time a member comes online or goes offline.
- Expect only some members in a large channel. A channel loads a limited number of members with its query; see [Channel details and members](https://getstream.io/chat/docs/sdk/react/v15/features/channels/channel-details-and-members/) for the full member list.

## Render the channel members list

The component reads the channel from `Channel` with `useChannel()`, and its members with `useChannelMembersState`:

```tsx title="src/ChannelMembers.tsx"
import { useChannel, useChannelMembersState } from "stream-chat-react";
import "./ChannelMembers.css";

export const ChannelMembers = () => {
  const channel = useChannel();
  const members = useChannelMembersState(channel);

  return (
    <ul className="channel-members">
      {Object.values(members).map((member) => (
        <li className="channel-members__item" key={member.user_id}>
          <span
            className={
              member.user?.online
                ? "channel-members__dot channel-members__dot--online"
                : "channel-members__dot"
            }
          />
          {member.user?.name ?? member.user_id}
        </li>
      ))}
    </ul>
  );
};
```

```css title="src/ChannelMembers.css"
.channel-members {
  margin: 0;
  padding: 12px 16px;
  border-bottom: 1px solid #ecebeb;
  list-style: none;
}

.channel-members__item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-block: 4px;
}

.channel-members__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #b4b7bb;
}

.channel-members__dot--online {
  background: #20e070;
}
```

Render it inside `Channel`, next to the other parts of the channel:

```tsx title="src/ChannelPanel.tsx"
import type { Channel as StreamChannel } from "stream-chat";
import {
  Channel,
  ChannelHeader,
  MessageComposer,
  MessageList,
} from "stream-chat-react";
import { ChannelMembers } from "./ChannelMembers";

export const ChannelPanel = ({ channel }: { channel: StreamChannel }) => (
  <Channel channel={channel}>
    <ChannelHeader />
    <ChannelMembers />
    <MessageList />
    <MessageComposer />
  </Channel>
);
```

With `ChatView`, put `ChannelMembers` among the children of `ChannelSlot` instead: the slot renders them inside the `Channel` it opens.

`channel.state.members` holds the loaded members by user id. Each member carries its `user`, with `name`, `image`, `online` and `last_active`. [What channels and threads hold](https://getstream.io/chat/docs/sdk/react/v15/state/channels-and-threads/what-channels-and-threads-hold/#data-and-members) lists the rest of `channel.state`.

## Real-time updates

The list updates by itself. When Bob comes online:

1. The server sends `user.presence.changed` with Bob's user, because the client watches a channel Bob is in with presence.
2. The client puts the new user object into Bob's member in `channel.state` of every channel it holds with him.
3. `channel.state` notifies its subscribers, and `useChannelMembersState` re-renders `ChannelMembers` with Bob online.

A member who joins, leaves or is updated changes the same store, through `member.added`, `member.removed` and `member.updated`, so the list follows those too.

The server sends presence changes only for channels watched with presence. Ask for it where the channel is loaded:

```ts
// A channel list: every channel it loads is watched with presence.
const teamChannels = new ChannelPaginator({
  client,
  filters: { members: { $in: [userId] }, type: "team" },
  id: "team",
  requestOptions: { presence: true },
});

// A channel you watch yourself.
const support = await client
  .channel("messaging", "support")
  .watch({ presence: true });
```

`Channel.watch({ presence: true })` sends a request even for a channel a channel list already watched without presence, because the current watch wasn't sent with these options. Later calls with the same options send nothing. [Channel list paginators](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/channel-list-paginators/#creating-a-channel-paginator) explains `requestOptions`, and [User presence](https://getstream.io/chat/docs/javascript/presence-format/) covers presence on the server.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/recipes/channel-list/members-and-online-status/](https://getstream.io/chat/docs/sdk/react/v15/recipes/channel-list/members-and-online-status/).