The "three dots" in a messenger is one of those small UX elements that users only notice when it's missing. The average indicator delay with a proper implementation is around 100 ms, but without optimization it can exceed 500 ms, causing irritation. Implementation seems trivial until you encounter debounce, cross-device consistency, and correct behavior under unstable connections. We have developed a mechanism that works on iOS, Android, and Flutter, and we are ready to adapt it to your project. Contact us — get a detailed implementation plan within one day.
How to implement a typing indicator in a chat?
The basic scheme: the client sends a typing_start event with each keystroke, the recipient sees the indicator, and after N seconds without new events, the indicator disappears. On paper it's simple. In practice, there are three typical issues.
Why debounce is critical — typing indicator implementation
Event flood. Without debounce, every onTextChanged (Android) or textDidChange (iOS) generates a network call. A user typing a message for 5 seconds creates 20–30 unnecessary requests. The right solution is to send typing_start only if more than 2–3 seconds have passed since the last send, and typing_stop with a delay of ~4 seconds after the last character.
On Android, implement with Handler.postDelayed or the debounce operator in Kotlin Flow:
private var typingJob: Job? = null fun onTextChanged(text: String) { if (text.isEmpty()) { sendTypingStop() return } typingJob?.cancel() typingJob = viewModelScope.launch { delay(TYPING_THROTTLE_MS) // 3000ms sendTypingStart() } } On iOS, similar logic via Timer.scheduledTimer or Combine:
private var typingCancellable: AnyCancellable? func textDidChange(_ text: String) { typingCancellable?.cancel() typingCancellable = Just(text) .delay(for: .seconds(3), scheduler: RunLoop.main) .sink { [weak self] _ in self?.sendTypingStart() } } How to choose the transport for typing events?
Transport layer. The typing indicator is an ephemeral state that does not require delivery guarantees. Therefore, REST/HTTP is overkill. The optimal choices are WebSocket or Firebase Realtime Database. Over WebSocket, send a lightweight JSON packet {"type":"typing","chat_id":"...","user_id":"..."}. Over Firebase, write to an ephemeral node /typing/{chatId}/{userId} with TTL via onDisconnect().removeValue(). The latter approach automatically cleans state on connection loss — which is critical.
Comparison of approaches:
| Criterion | WebSocket | Firebase RTDB |
|---|---|---|
| Latency | 2–5 ms | 20–50 ms |
| Auto-cleanup on disconnect | Requires heartbeat | Built-in (onDisconnect) |
| Integration complexity | Medium | Low |
| Server load | High (persistent connection) | Low (managed by Firebase) |
How to animate the indicator on different platforms?
Animation of the three dots: on iOS — custom CABasicAnimation or Lottie, on Android — AnimationDrawable or LottieAnimationView. Flutter solves this with AnimatedOpacity + Timer. Important: the animation must be lightweight and not consume CPU during long display — use a timer to pause.
Comparison of animation options:
| Platform | Approach | Performance | Complexity |
|---|---|---|---|
| iOS | CABasicAnimation | 60 FPS | Low |
| Android | LottieAnimationView | 60 FPS | Medium |
| Flutter | AnimatedOpacity + Transform | 60 FPS | Medium |
Display on the receiver
After receiving the event, display the indicator and start a cleanup timer (~5 seconds). If a new event arrives during that time, reset the timer. A typical mistake is forgetting to update the timer, causing the indicator to disappear early. We recommend using a single timer in the ViewModel that is canceled and restarted with each new event. Ensure the app handles connection loss correctly: on reconnection, it must re-subscribe to chat events.
How to avoid App Store moderation issues?
Apple strictly checks the use of background modes and push notifications. If the typing indicator requires a persistent connection, specify the "Voice over IP" background mode or use WebSocket with NSURLSessionWebSocketTask. To comply with sections 4.2 and 5.1, add a user consent checkbox. We guarantee first-time review approval — our engineers will account for all nuances.
What our work includes
- Audit of the current transport layer: WebSocket, Firebase, XMPP — we evaluate performance and reliability.
- Development of debounce logic for the target platform, tested on edge cases (fast typing, text deletion, connection loss).
- Implementation of indicator animation following platform guidelines.
- Integration with the server side: API adjustments, Firebase configuration, WebSocket setup.
- Testing on real devices and Simulator/Emulator.
- Provision of documentation and a ready module for embedding into the project.
Timeline: from 1 to 3 days depending on the complexity of the current chat architecture. Order the implementation — get a working prototype in the shortest time.
Our experience
We have been working with mobile applications for over 5 years and implemented chats for 15+ projects with a total audience of over 500,000 users. We know the typical pitfalls of the App Store Review and guarantee first-time approval.
To get a consultation or order the implementation of a typing indicator for your chat, contact us — we will assess the project and offer a solution tailored to your stack. Get a detailed implementation plan within one day.







