# Creating Channels

Channels must be created before users can start chatting. Channel creation can occur either client-side or server-side, depending on your app’s requirements. Client-side creation is ideal for apps where users can freely start conversations (for example, a Slack-style workforce management app). Server-side creation is preferred in apps that require business logic before a chat can begin, such as dating apps where users must match first. To limit channel creation to server-side only, remove Create Channel [permissions](/chat/docs/<framework>/chat_permission_policies/) for your users.

There are two ways to create channels: by specifying a channel ID or by creating distinct channels.

## Creating a Channel Using a Channel ID

This approach works best when your app already has a database object that naturally maps to a chat channel. For example, in a Twitch-style live-streaming service, each streamer has a unique ID you can reuse as the channel ID, making it easy to route users to the correct chat. Using explicit IDs keeps channels predictable and easy to reference throughout your application.

<codetabs>

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

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

channelClient.create(memberIds = emptyList(), extraData = emptyMap()).enqueue { result ->
  if (result is Result.Success) {
    val newChannel: Channel = result.value
  } else {
     // Handle Result.Failure
  }
}
```

</codetabs-item>

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

```js
const channel = client.channel("messaging", "travel", {
  name: "Awesome channel about traveling",
});
// Here, 'travel' will be the channel ID
await channel.create();
```

</codetabs-item>

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

```swift
/// 1: Create a `ChannelId` that represents the channel you want to create.
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 create the channel.
channelController.synchronize { error in
  if let error = error {
    /// 4: Handle possible errors
    print(error)
  }
}
```

</codetabs-item>

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

```dart
final channel = client.channel(
 "messaging",
 id: "travel",
 extraData: {
  "name": "Founder Chat",
  "image": "http://bit.ly/2O35mws",
  "members": ["thierry", "tommaso"],
 },
);
```

</codetabs-item>

<codetabs-item value="nodejs" label="Node">

```js
const channel = client.channel("messaging", "travel", {
  name: "Awesome channel about traveling",
  created_by_id: "myuserid",
});
await channel.create();
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
# require 'stream-chat'

# Note: 'channel_id' parameter must be named!
channel = client.channel('messaging', channel_id: 'general')

channel.create('jlahey')
```

</codetabs-item>

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

```cpp
const FChannelProperties Properties{
  TEXT("messaging"),  // Type
  TEXT("travel"),    // Id
};
Client->CreateChannel(
  Properties,
  [](UChatChannel* Channel)
  {
    // Channel created
  });
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
channel = client.channel("messaging", "travel")
channel.create("myuserid")
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
$channel = $client->Channel("messaging", "general");
$channel->create("thierry");
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
var chanData = new ChannelRequest { CreatedBy = new UserRequest { Id = "thierry" } };
await channelClient.GetOrCreateAsync("messaging", "general", new ChannelGetRequest
{
  Data = chanData,
});
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
client.CreateChannel(ctx, "messaging", "general", "tommaso", nil)
```

</codetabs-item>

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

```csharp
var channel = await Client.GetOrCreateChannelWithIdAsync(ChannelType.Messaging, "my-channel-id");

//Once you get or query a channel it is also added to Client.WatchedChannels list
```

</codetabs-item>

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

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

Map<String, Object> extraData = new HashMap<>();
List<String> memberIds = new LinkedList<>();

channelClient.create(memberIds, extraData)
    .enqueue(result -> {
      if (result.isSuccess()) {
        Channel newChannel = result.data();
      } else {
       // Handle result.error()
     }
   });

// Backend SDK
ChannelGetResponse channel = Channel.getOrCreate("messaging", "general")
  .data(
    ChannelRequestObject.builder()
      .createdBy(someUser)
      .additionalField("custom_field", "custom_value") // Optional custom fields
      .build())
  .request()
```

</codetabs-item>

</codetabs>

## Distinct Channels

Distinct channels are ideal when you want a single, unique conversation for a specific set of users. By leaving the channel ID empty and specifying only the channel type and members, Stream automatically generates a channel ID by hashing the list of members (order does not matter). This ensures that the same group of users will always reference the same channel, preventing duplicate conversations.

<admonition type="info">

You cannot add members for channels created this way, but members can be removed.

</admonition>

<codetabs>

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

```kotlin
client.createChannel(
  channelType = "messaging",
  channelId = "",
  memberIds = listOf("thierry", "tomasso"),
  extraData = emptyMap()
).enqueue { result ->
   if (result is Result.Success) {
     val channel = result.value
   } else {
     // Handle Result.Failure
   }
}
```

</codetabs-item>

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

```js
const channel = client.channel("messaging", {
  members: ["thierry", "tommaso"],
});
await channel.create();
```

</codetabs-item>

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

```dart
client.channel("messaging", extraData: {"members": ["thierry", "tommaso"]});
```

</codetabs-item>

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

```swift
/// 1: Use the `ChatClient` to create a `ChatChannelController` with a list of user ids
let channelController = try chatClient.channelController(
  createDirectMessageChannelWith: ["thierry", "tomasso"],
  extraData: [:]
)

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

</codetabs-item>

<codetabs-item value="nodejs" label="Node">

```js
const channel = client.channel("messaging", {
  members: ["thierry", "tommaso"],
  created_by_id: "myuserid",
});
await channel.create();
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
$conversation = $client->Channel('messaging', 'thierry-jenny', ['members'=>['thierry', 'jenny']]);
$state  = $conversation->create('thierry');
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
# Note: 'channel_id' and 'data' parameters must be named!

channel = client.channel(
  'messaging',
  channel_id: 'thierry-tommaso-1',
  data: {
   'name'  => 'Founder Chat',
   'image' => 'http://bit.ly/2O35mws',
   'members' => ['thierry', 'tommaso'],
  }
)

channel.create('thierry')
```

</codetabs-item>

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

```cpp
const FChannelProperties Properties = FChannelProperties{TEXT("messaging")}
            .SetMembers({TEXT("thierry"), TEXT("tommaso")});
Client->CreateChannel(
  Properties,
  [](UChatChannel* Channel)
  {
    // Channel created
  });
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
channel = client.channel("messaging", data=dict(members=["thierry"]))
channel.create("myuserid")
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
$channel = $client->Channel(
  "messaging",
  null,
  ["members" => ["thierry", "tommaso"]]
);
$channel->create("thierry");
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
var chanData = new ChannelRequest { CreatedBy = new UserRequest { Id = "thierry" } };
await channelClient.GetOrCreateAsync("messaging", new ChannelGetRequest
{
  Data = chanData,
});
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
client.CreateChannel(ctx, "messaging", nil, "tommaso", map[string]interface{}{
		"members": []string{"thierry", "tommaso"},
})
```

</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 a chat with
var users = await Client.QueryUsersAsync(filters);

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

// Start direct channel between 2 users
var channel = await Client.GetOrCreateChannelWithMembersAsync(ChannelType.Messaging, new[] { localUser, otherUser});
```

</codetabs-item>

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

```java
// Android SDK
ChannelClient channelClient = client.channel("messaging", "");

Map<String, Object> extraData = new HashMap<>();
List<String> memberIds = new LinkedList<>();
memberIds.add("thierry");
memberIds.add("tomasso");

channelClient.create(memberIds, extraData)
    .enqueue(result -> {
      if (result.isSuccess()) {
        Channel newChannel = result.data();
      } else {
       // Handle result.error()
     }
   });

// Backend SDK
List<ChannelMemberRequestObject> memberList = new ArrayList<>();
testUsersRequestObjects.add(
  ChannelMemberRequestObject.builder().user(user1).build());
testUsersRequestObjects.add(
  ChannelMemberRequestObject.builder().user(user2).build());

ChannelGetResponse channel = Channel.getOrCreate("messaging", null)
  .data(
    ChannelRequestObject.builder()
      .createdBy(someUser)
      .members(memberList)
      .build())
  .request()
```

</codetabs-item>

</codetabs>

When you create a channel using one of the above approaches, you'll specify the following fields:

| name         | type   | description                                                                                                                             | default | optional |
| ------------ | ------ | --------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------- |
| type         | string | The channel type. Default types are livestream, messaging, team, gaming and commerce. You can also create your own types.               | -       |          |
| id           | string | The channel id (optional). If you don't specify an ID the ID will be generated based on the list of members. (max length 64 characters) | -       | ✓        |
| channel data | object | Extra data for the channel. Must not exceed 5KB in size.                                                                                | default | ✓        |

## Channel Data

Channel data can include any number of custom fields as long as the total payload stays under 5KB. Some fields are reserved—such as `members`—and our UI components also use `name` and `image` when rendering channel lists and headers. In general, you should store only the data that's essential to your chat experience and avoid adding fields that change frequently.

| Name        | Type          | Description                                                                                                                                                                                                                                                         |
| ----------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name        | string        | The channel name. No special meaning, but by default the UI component will try to render this if the property is present.                                                                                                                                           |
| image       | string        | The channel image. Again there is no special meaning but by default, the UI component will try to render this if the property is present.                                                                                                                           |
| members     | array         | The members participating in this Channel. Note that you don't need to specify members for a live stream or other public chat. You only need to specify the members if you want to limit access of this chat to these members and subscribe them to future updates. |
| custom_data | various types | Channels can contain up to 5KB of custom data.                                                                                                                                                                                                                      |

## Watching Channels

Once a channel exists—or as it is being created on the client—it can be watched by client devices. Watching a channel subscribes the client’s WebSocket connection to real-time updates, such as new messages, membership changes, and reactions. This allows the SDKs to keep channel state and UI in sync automatically.

If your app lets users navigate to a channel client-side, you should use `channel.watch()`. This is a get-or-create method: it fetches and watches the channel if it already exists, or creates and watches it if it doesn't. `channel.watch()` returns the full channel state—including members, watchers, and messages—so your UI can render immediately.

For loading many channels at once, use [client.queryChannels()](/chat/docs/<framework>/query_channels/)—this fetches and watches multiple channels in a single call, reducing API traffic.

Note that watching a channel is different from being a channel member. A watcher is a temporary, real-time subscription to updates, while a member is a persistent association with the 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 channel = client.channel("messaging", "travel-channel");

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);

// 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>


---

This page was last updated at 2026-03-05T19:06:11.725Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/react/creating_channels/](https://getstream.io/chat/docs/react/creating_channels/).