# Typing Indicators

All official SDKs support typing events out of the box and are handled on all channels with the typing_events featured enabled. Typing indicators allow you to show to users who is currently typing in the channel. This feature can be switched on/off on a channel-type basis using the CLI or directly from the Dashboard. If you are using one of the official SDK libraries, you will only need to ensure that typing indicators are enabled to get this working.

If you are building your UI on top of one of our Chat Clients instead, you will need to take care of four things:

1. Send an event `typing.start` when the user starts typing

2. Send an event `typing.stop` after the user stopped typing

3. Handle the two events and use them to toggle the typing indicator UI

4. Use `parent_id` field of the event to indicate that typing is happening in a thread

### Sending start and stop typing events

<codetabs>

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

```js
// The JS client keeps track of the typing state for you.
// Just call `channel.keystroke()` when the user types and
// `channel.stopTyping()` when the user sends the message (or aborts)

// sends a typing.start event at most once every two seconds
await channel.keystroke();

// sends a typing.start event for a particular thread
await channel.keystroke(thread_id);

// sends the typing.stop event
await channel.stopTyping();
```

</codetabs-item>

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

```dart
// The Dart client keeps track of the typing state for you.
// Just call `channel.keystroke()` when the user types and
// `channel.stopTyping()` when the user sends the message (or aborts)

// sends a typing.start event at most once every two seconds
await channel.keystroke();

// sends a typing.start event for a particular thread
await channel.keystroke(thread_id);

// sends the typing.stop event
await channel.stopTyping();
```

</codetabs-item>

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

```kotlin
// Sends a typing.start event at most once every two seconds
channelClient.keystroke().enqueue()

// Sends a typing.start event for a particular thread
channelClient.keystroke(parentId = "threadId").enqueue()

// Sends the typing.stop event
channelClient.stopTyping().enqueue()// Automatically
```

</codetabs-item>

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

```swift
// The Swift client keeps track of the typing state for you.
// Just call `sendKeystrokeEvent()` when the user types and

// sends a typing.start event at most once every two seconds
// automatically sends typingStop event after 15 seconds
let channelController = client.channelController(for: .init(type: .messaging, id: "general"))

// automatically
channelController.sendKeystrokeEvent()

// or manually
channelController.sendStartTypingEvent()
channelController.sendStopTypingEvent()
```

</codetabs-item>

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

```cpp
// The Unreal SDK keeps track of the typing state for you.
// Just call `Channel->Keystroke()` when the user types and
// `Channel->StopTyping()` when the user sends the message (or aborts)

// Sends a typing.start event at most once every two seconds,
// and a typing.stop event two seconds after the last keystroke
Channel->KeyStroke();

// Manually sends the typing.stop event, and cancels any pending
// typing.stop event queued by Keystroke()
Channel->StopTyping();
```

</codetabs-item>

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

```java
// Sends a typing.start event
channelClient.keystroke().enqueue();

// Sends a typing.start event for a particular thread
channelClient.keystroke("threadId").enqueue();

// Sends the typing.stop event
channelClient.stopTyping().enqueue();
```

</codetabs-item>

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

```csharp
// Send typing started event
await channel.SendTypingStartedEventAsync();

// Send typing stopped event
await channel.SendTypingStoppedEventAsync();
```

</codetabs-item>

</codetabs>

When sending events on user input, you should make sure to follow some best-practices to avoid bugs.

1. Only send `typing.start` when the user starts typing

2. Send `typing.stop` after a few seconds since the last keystroke

### Receiving typing indicator events

<codetabs>

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

```js
// channels keep track of the users that are currently typing.
// `channel.state.typing` is an immutable object which gets regenerated
// every time a new user is added or removed to this list
console.log(channel.state.typing);

// start typing event handling
channel.on("typing.start", (event) => {
  if (event.parent_id) {
    console.log(
      `${event.user.name} started typing in thread ${event.parent_id}`,
    );
  } else {
    console.log(`${event.user.name} started typing`);
  }
});

// stop typing event handling
channel.on("typing.stop", (event) => {
  if (event.parent_id) {
    console.log(
      `${event.user.name} stopped typing in thread ${event.parent_id}`,
    );
  } else {
    console.log(`${event.user.name} stopped typing`);
  }
});
```

</codetabs-item>

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

```dart
// channels keep track of the users that are currently typing
// the `channel.state.typingEvents` is an immutable object which gets regenerated
// every time a new user is added or removed to this list
print(channel.state.typingEvents);

// add typing start event handling
channel.on('typing.start', (event) => {
 print('${event.user.name} started typing');
});

// add typing stop event handling
channel.on('typing.stop', event => {
 print('${event.user.name} stopped typing');
});
```

</codetabs-item>

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

```kotlin
// Add typing start event handling
channelClient.subscribeFor<TypingStartEvent> { typingStartEvent ->
  // Handle event
}

// Add typing stop event handling
channelClient.subscribeFor<TypingStopEvent> { typingStopEvent ->
  // Handle event
}
```

</codetabs-item>

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

```swift
let channelController = client.channelController(for: .init(type: .messaging, id: "general"))

class ChannelDelegate: ChatChannelControllerDelegate {
  func channelController(_ channelController: ChatChannelController,
              didChangeTypingUsers typingUsers: Set<ChatUser>) {
    // handle new typing users
  }
}


channelController.delegate = ChannelDelegate()

channelController.synchronize()
```

</codetabs-item>

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

```cpp
// You can manually subscribe to the typing channel events
Channel->On<FTypingStartEvent>(this, &UMyWidget::OnTypingStart);
Channel->On<FTypingStopEvent>(this, &UMyWidget::OnTypingStop);

// Alternatively you can bind to the OnTypingIndicator delegate (also from Blueprints)
Channel->OnTypingIndicator.AddDynamic(this, &UMyWidget::OnTypingIndicator);
```

</codetabs-item>

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

```java
// Add typing start event handling
channelClient.subscribeFor(
   new Class[]{TypingStartEvent.class},
   event -> {
     // Handle change
   }
);

// Add typing stop event handling
channelClient.subscribeFor(
   new Class[]{TypingStopEvent.class},
   event -> {
     // Handle change
   }
);
```

</codetabs-item>

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

```csharp
public async Task ReceivingTypingEvents()
{
  var channel = await Client.GetOrCreateChannelWithIdAsync(ChannelType.Messaging, "channel-id");
  channel.UserStartedTyping += OnUserStartedTyping;
  channel.UserStoppedTyping += OnUserStoppedTyping;
}

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

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

</codetabs-item>

</codetabs>

<admonition type="info">

Because clients might fail at sending `typing.stop` event all Chat clients periodically prune the list of typing users.

</admonition>

### Control typing events delivery by modifying user privacy settings

Please take into account that `typing.start` and `typing.stop` events delivery can be controlled by user privacy settings:

<codetabs>

<codetabs-item value="json" label="JSON">

```json
// user object with privacy settings where typing indicators are disabled
{
  // other user fields
  "privacy_settings": {
    "typing_indicators": {
      "enabled": false
    }
  }
}
```

</codetabs-item>

</codetabs>

If `privacy_settings.typing_indicators.enabled` is set to `false` , then `typing.start` and `typing.stop` events will be ignored for this user by Stream's server and these events will not be sent to other users. In other words other users will not know that the current user was typing.

---

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

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