# Watching a Channel

The call to **channel.watch** does a few different things in one API call:

- It creates the channel if it doesn't exist yet (if this user has the right permissions to create a channel)

- It queries the channel state and returns members, watchers and messages

- It watches the channel state and tells the server that you want to receive events when anything in this channel changes

### To start watching a channel

The examples below show how to watch a channel. Note that you need to be connected as a user before you can watch a channel.

<codetabs>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
val channelClient = client.channel(channelType = "messaging", channelId = "general")

channelClient.watch().enqueue { result ->
  if (result is Result.Success) {
    val channel: Channel = result.value
  } else {
    // Handle Result.Failure
  }
}
```

</codetabs-item>

<codetabs-item value="javascript" label="JavaScript">

```js
const state = await channel.watch();
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
final state = await channel.watch();
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
/// Channels are watched automatically when they're synchronized.

/// 1: Create a `ChannelId` that represents the channel you want to watch.
let channelId = ChannelId(type: .messaging, id: "general")

/// 2: Use the `ChatClient` to create a `ChatChannelController` with the `ChannelId`.
let channelController = chatClient.channelController(for: channelId)

/// 3: Call `ChatChannelController.synchronize` to watch the channel.
channelController.synchronize { error in
  if let error = error {
    /// 4: Handle possible errors
    print(error)
  }
}
```

</codetabs-item>

<codetabs-item value="unreal" label="Unreal">

```cpp
const FChannelProperties Properties{
  TEXT("messaging"),  // Type
  TEXT("general"),   // Id
};
Client->WatchChannel(
  Properties,
  [](UChatChannel* Channel)
  {
    // Started watching channel
  });
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
var filters = new IFieldFilterRule[]
{
  UserFilter.Id.EqualsTo("other-user-id")
};
// find user you want to start chat with
var users = await Client.QueryUsersAsync(filters);

var otherUser = users.First();
var localUser = Client.LocalUserData.User;

// Get channel by ID
var channel = await Client.GetOrCreateChannelWithIdAsync(ChannelType.Messaging, channelId: "my-channel-id");

// Get channel with users combination
var channelWithUsers
  = await Client.GetOrCreateChannelWithMembersAsync(ChannelType.Messaging,
    new[] { localUser, otherUser });

// Access properties
Debug.Log(channel.Name);
Debug.Log(channel.Members);
Debug.Log(channel.Name);
Debug.Log(channel.Name);

// Subscribe to events so you can react to updates
channel.MessageReceived += OnMessageReceived;
channel.MessageUpdated += OnMessageUpdated;
channel.MessageDeleted += OnMessageDeleted;
channel.ReactionAdded += OnReactionAdded;
channel.ReactionUpdated += OnReactionUpdated;
channel.ReactionRemoved += OnReactionRemoved;

// You can also access all currently watched channels via
foreach (var c in Client.WatchedChannels)
{
  // Every queried channel is automatically watched and starts receiving updates from the server
}
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
ChannelClient channelClient = client.channel("messaging", "general");

channelClient.watch().enqueue(result -> {
  if (result.isSuccess()) {
    Channel channel = result.data();
  } else {
    // Handle result.error()
  }
});
```

</codetabs-item>

</codetabs>

Response Schema

| Name     | Type    | Description                                                                                         |
| -------- | ------- | --------------------------------------------------------------------------------------------------- |
| config   | object  | The configuration for the channel type.                                                             |
| channel  | object  | The Channel object.                                                                                 |
| online   | integer | Number of online members.                                                                           |
| watchers | object  | Users that are watching this channel. Represented as a mapping from the user id to the user object. |
| members  | object  | Channel members. Represented as a mapping from the user id to the user object.                      |
| read     | object  | Read messages grouped by user id. Represented as a mapping from the user id to the message object.  |

<admonition type="info">

Watching a channel only works if you have connected as a user to the chat API

</admonition>

### Watchers vs Members

The concepts of watchers vs members can require a bit of clarification:

- **members** : a permanent association between a user and a channel. If the user is online and not watching the channel they will receive a notification event, if they are offline they will receive a push notification.

- **watchers** : the list of watchers is temporary. It's anyone who is currently watching the channel.

Being able to send messages, and otherwise engage with a channel as a non-member requires certain permissions. For example, we have pre-configured permissions on our livestream channel type to allow non-members to interact, but in the messaging channel type, only members of the channel can interact.

### Watching Multiple Channels

The default [queryChannels](/chat/docs/<framework>/query_channels/) API returns channels and starts watching them. There is no need to also use channel.watch on the channels returned from queryChannels

<codetabs>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
val request = QueryChannelsRequest(
  filter = Filters.and(
    Filters.eq("type", "messaging"),
    Filters.`in`("members", listOf(currentUserId)),
  ),
  offset = 0,
  limit = 10,
  querySort = QuerySortByField.descByName("last_message_at")
).apply {
  // Watches the channels automatically
  watch = true
  state = true
}

// Run query on ChatClient
client.queryChannels(request).enqueue { result ->
  if (result is Result.Success) {
    val channels: List<Channel> = result.value
  } else {
    // Handle Result.Failure
  }
}
```

</codetabs-item>

<codetabs-item value="javascript" label="JavaScript">

```js
// first let’s create a filter to make messaging channels that include a specific user
const filter = { type: "messaging", members: { $in: [user_id] } };
// we can also define a sort order of most recent messages first
const sort = { last_message_at: -1 };

// finally, we can query for those channels, automatically watching them for the
// currently connected user
const channels = await client.queryChannels(filter, sort, { watch: true });
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
// first let’s create a filter to make messaging channels that include a specific user
final filter = {
 "type": "messaging",
 "members": {
  "\$in": [user_id]
 }
};

// we can also define a sort order of most recent messages first
final sort = [SortOption("last_message_at", direction: SortOption.DESC)];

// finally, we can query for those channels, automatically watching them for the
// currently connected user
final channels = await client.queryChannels(
 filter: filter,
 sort: sort,
 options: {
  "watch": true,
  "state": true,
 },
);
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
/// Channels are watched automatically when they're synchronized.
let channelListController = client.channelListController(
  query: .init(
    filter: .and(
      [.equal(.type, to: .messaging),
       .containMembers(userIds: [user_id])
      ]
    ),
  sort: [.init(key: .lastMessageAt, isAscending: true)]
  )
)

/// Call `synchronize` to watch the channel.
channelListController.synchronize { error in
  if let error = error {
    /// 4: Handle possible errors
    print(error)
  }
}
```

</codetabs-item>

<codetabs-item value="unreal" label="Unreal">

```cpp
const FString UserId = TEXT("my-user-id");
const FFilter Filter = FFilter::In(TEXT("members"), {UserId});
const TArray<FChannelSortOption> SortOptions{{EChannelSortField::LastMessageAt, ESortDirection::Descending}};
Client->QueryChannels(
  Filter,
  SortOptions,
  EChannelFlags::State | EChannelFlags::Watch,  // Flags
  {},                       // Pagination Options
  [](const TArray<UChatChannel*> ReceivedChannels)
  {
    // Started watching channels
  });
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
var localUser = Client.LocalUserData.User;

var filters = new IFieldFilterRule[]
{
  // Get channels where local user is a member
  ChannelFilter.Members.In(localUser.Id)
};

var channels = await Client.QueryChannelsAsync(filters);

// Get all currently watched channels
foreach (var channel in channels)
{
  // Access properties
  Debug.Log(channel.Name);
  Debug.Log(channel.Members);
  Debug.Log(channel.Name);
  Debug.Log(channel.Name);

  // Subscribe to events so you can react to updates
  channel.MessageReceived += OnMessageReceived;
  channel.MessageUpdated += OnMessageUpdated;
  channel.MessageDeleted += OnMessageDeleted;
  channel.ReactionAdded += OnReactionAdded;
  channel.ReactionUpdated += OnReactionUpdated;
  channel.ReactionRemoved += OnReactionRemoved;
}
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
FilterObject filter = Filters.and(
    Filters.eq("type", "messaging"),
    Filters.in("members", Arrays.asList(currentUserId))
);
int offset = 0;
int limit = 10;
QuerySortByField<Channel> sort = QuerySortByField.descByName("last_message_at");
int messageLimit = 0;
int memberLimit = 0;

QueryChannelsRequest request = new QueryChannelsRequest(filter, offset, limit, sort, messageLimit, memberLimit)
    .withWatch() // Watches the channels automatically
    .withState();

// Run query on ChatClient
client.queryChannels(request).enqueue((result) -> {
  if (result.isSuccess()) {
    List<Channel> channels = result.data();
  } else {
    // Handle result.error()
  }
});
```

</codetabs-item>

</codetabs>

### Stop Watching a Channel

To stop receiving channel events:

<codetabs>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
channelClient.stopWatching().enqueue { result ->
  if (result.isSuccess) {
    // Channel unwatched
  } else {
    // Handle Result.Failure
  }
}
```

</codetabs-item>

<codetabs-item value="javascript" label="JavaScript">

```js
// we can also stop watching a channel
const stopWatching = await channel.stopWatching();
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
// we can also stop watching a channel
final stopWatching = await channel.stopWatching();
```

</codetabs-item>

<codetabs-item value="unreal" label="Unreal">

```cpp
Channel->StopWatching();
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
await channel.StopWatchingAsync();
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
channelClient.stopWatching().enqueue(result -> {
  if (result.isSuccess()) {
    // Channel unwatched
  } else {
    // Handle result.error()
  }
});
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
channelController.stopWatching { error in
  // …
}
```

</codetabs-item>

</codetabs>

### Watcher Count

To get the watcher count of a channel:

<codetabs>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
val request = QueryChannelRequest().withState()
channelClient.query(request).enqueue { result ->
  if (result is Result.Success) {
    val channel: Channel = result.value
    channel.watcherCount
  } else {
    // Handle Result.Failure
  }
}
```

</codetabs-item>

<codetabs-item value="javascript" label="JavaScript">

```js
// filter on a specific channel cid
const filter = { cid: channelCID };
// sort by most recent messages first
const sort = { last_message_at: -1 };

// retrieve our channels
const channels = await client.queryChannels(filter, sort);

// each channel object has a state collection with a watcher_count property
return channels[0].state.watcher_count;
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
// create a new channel of type “livestream” with name “watch-this-channel”
final channel = client.channel("livestream", id: "watch-this-channel");

// retrieve our channels
channel.query();

// each channel object has a state collection with a watcher_count property
 return channel.state.watcherCount;
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
/// Channels are watched automatically when they're synchronized.

/// 1: Create a `ChannelId` that represents the channel you want to watch.
let channelId = ChannelId(type: .messaging, id: "general")

/// 2: Use the `ChatClient` to create a `ChatChannelController` with the `ChannelId`.
client.channelController(for: .init(cid: channelId, watchersLimit: 25))

/// 3: Call `ChatChannelController.synchronize` to watch the channel.
channelController.synchronize { error in
  if let error = error {
    /// 4: Handle possible errors
    print(error)
  }
  // Access watcherCount
  channelController.channel?.watcherCount
}
```

</codetabs-item>

<codetabs-item value="unreal" label="Unreal">

```cpp
const FChannelProperties Properties{
  TEXT("livestream"),      // Type
  TEXT("watch-this-channel"),  // Id
};
Client->WatchChannel(
  Properties,
  [](const UChatChannel* Channel)
  {
    // Do something with:
    Channel->State.WatcherCount;
  });
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
Debug.Log(channel.WatcherCount);
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
QueryChannelRequest request = new QueryChannelRequest().withState();
channelClient.query(request).enqueue((result) -> {
  if (result.isSuccess()) {
    Channel channel = result.data();
    channel.getWatcherCount();
  } else {
    // Handle result.error()
  }
 });
```

</codetabs-item>

</codetabs>

### Paginating Channel Watchers with channel.query

<codetabs>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
val request = QueryChannelRequest()
  .withWatchers(limit = 5, offset = 0)
channelClient.query(request).enqueue { result ->
  if (result is Result.Success) {
    val channel: Channel = result.value
    val watchers: List<User> = channel.watchers
  } else {
    // Handle Result.Failure
  }
}
```

</codetabs-item>

<codetabs-item value="javascript" label="JavaScript">

```js
// create a new channel of type “livestream” with name “watch-this-channel”
const channel = client.channel("livestream", "watch-this-channel", {});

// now query the newly created channel for watchers, retrieving the first 5
const result = await channel.query({
  watchers: { limit: 5, offset: 0 },
});

return result.watchers;
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
// create a new channel of type “livestream” with name “watch-this-channel”
final channel = client.channel("livestream", id: "watch-this-channel");

// now query the newly created channel for watchers, retrieving the first 5
final result = await channel.query(
  watchersPagination: PaginationParams(
   limit: 5,
   offset: 0,
  ),
 );

 return result.watchers;
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
/// Channels are watched automatically when they're synchronized.

/// 1: Create a `ChannelId` that represents the channel you want to watch.
let channelId = ChannelId(type: .messaging, id: "general")

/// 2: Use the `ChatClient` to create a `ChatChannelController` with the `ChannelId`.
client.channelController(for: .init(cid: channelId, watchersLimit: 25))

/// 3: Call `ChatChannelController.synchronize` to watch the channel.
channelController.synchronize { error in
  if let error = error {
    /// 4: Handle possible errors
    print(error)
  }
  // Access watcherCount
  channelController.channel?.lastActiveWatchers
}
```

</codetabs-item>

<codetabs-item value="unreal" label="Unreal">

```cpp
// Query the first 5 watchers of a channel
const FUserPaginationOptions WatcherPagination{
  5,  // Limit
  0,  // Offset
};
Channel->Query(
  EChannelFlags::State,  // Flags
  {},           // Message Pagination
  {},           // Member Pagination
  WatcherPagination,
  []
  {
    // Do something with:
    Channel->State.Watchers;
  });
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
// Will be implemented soon, raise a GitHub issue if you need this https://github.com/GetStream/stream-chat-unity/issues/new
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
int limit = 5;
int offset = 0;
QueryChannelRequest request = new QueryChannelRequest().withWatchers(limit, offset);
channelClient.query(request).enqueue((result) -> {
  if (result.isSuccess()) {
    Channel channel = result.data();
    List<User> watchers = channel.getWatchers();
  } else {
    // Handle result.error()
  }
});
```

</codetabs-item>

</codetabs>

The maximum limit that can be used is 300, and the maximum offset that can be used is 10 000.

### Listening to Changes in Watchers

A user already watching the channel can listen to users starting and stopping watching the channel with the realtime events:

<codetabs>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
// Start watching channel
channelClient.watch().enqueue {
  /* Handle result */
}

// Subscribe for watching events
channelClient.subscribeFor(
  UserStartWatchingEvent::class,
  UserStopWatchingEvent::class,
) { event ->
  when (event) {
    is UserStartWatchingEvent -> {
      // User who started watching the channel
      val user = event.user
    }
    is UserStopWatchingEvent -> {
      // User who stopped watching the channel
      val user = event.user
    }
    else -> { /* not possible */ }
  }
}
```

</codetabs-item>

<codetabs-item value="javascript" label="JavaScript">

```js
const channel = client.channel("livestream", "watch-this-channel", {});

channel.watch();

channel.on("user.watching.start", (event) => {
  // handle watch started event
  console.log(`${event.user.id} started watching`);
});
channel.on("user.watching.stop", (event) => {
  // handle watch stopped event
  console.log(`${event.user.id} stopped watching`);
});
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
final channel = client.channel("livestream", id: "watch-this-channel");

await channel.watch();

// handle watch started event
channel
   .on("user.watching.start")
   .listen((event) => print('${event.user.id} started watching'));

// handle watch stopped event
channel
   .on("user.watching.stop")
   .listen((event) => print('${event.user.id} stopped watching'));
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
let controller = client.watcherListController(query: .init(cid: .init(type: .messaging, id: "general")))

class MyChannelWatcherListDelegate: ChatChannelWatcherListControllerDelegate {
  func channelWatcherListController(_ controller: ChatChannelWatcherListController, didChangeWatchers changes: [ListChange<ChatUser>]) {
    // handle change
  }
}

controller.delegate = MyChannelWatcherListDelegate()

controller.synchronize()
```

</codetabs-item>

<codetabs-item value="unreal" label="Unreal">

```cpp
Channel->On<FUserWatchingStartEvent>(
  [](const FUserWatchingStartEvent& Event)
  {
    // Do something with:
    Event.User;
  });

Channel->On<FUserWatchingStopEvent>(
  [](const FUserWatchingStopEvent& Event)
  {
    // Do something with:
    Event.User;
  });
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
public async Task ListenToChangesInWatchers()
{
  // Get IStreamChannel reference by Client.GetOrCreateChannel or Client.QueryChannels
  var channel = await Client.GetOrCreateChannelWithIdAsync(ChannelType.Messaging, channelId: "my-channel-id");

  // Subscribe to events
  channel.WatcherAdded += OnWatcherAdded;
  channel.WatcherRemoved += OnWatcherRemoved;
}

private void OnWatcherAdded(IStreamChannel channel, IStreamUser user)
{
}

private void OnWatcherRemoved(IStreamChannel channel, IStreamUser user)
{
}
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
// Start watching channel
channelClient.watch().enqueue((result) -> {
  /* Handle result */
});

// Subscribe for watching events
channelClient.subscribeFor(
    new Class[]{
        UserStartWatchingEvent.class,
        UserStopWatchingEvent.class,
    },
    (event) -> {
      if (event instanceof UserStartWatchingEvent) {
        // User who started watching the channel
        User user = ((UserStartWatchingEvent) event).getUser();
      } else if (event instanceof UserStopWatchingEvent) {
        // User who stopped watching the channel
        User user = ((UserStopWatchingEvent) event).getUser();
      }
    }
);
```

</codetabs-item>

</codetabs>

---

This page was last updated at 2025-08-15T14:14:30.151Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/go-golang/watch_channel/](https://getstream.io/chat/docs/go-golang/watch_channel/).