The MessageReactions interface provides methods to send, delete, and subscribe to reactions on specific messages. Unlike room reactions, message reactions are persisted and associated with individual chat messages. Access it via room.messages.reactions.
1
val reactions = room.messages.reactionsMessage reactions support three counting strategies:
- Unique: One reaction per client per message (like iMessage, WhatsApp)
- Distinct: One reaction of each type per client (like Slack)
- Multiple: Unlimited reactions including repeats (like Medium claps)
Send a reaction to a message
suspend reactions.send(messageSerial: String, name: String, type: MessageReactionType? = null, count: Int? = null): UnitSends a reaction to a specific chat message. The reaction is persisted and contributes to the message's reaction summary.
This method uses the Ably Chat REST API and does not require the room to be attached.
1
2
3
4
room.messages.reactions.send(
messageSerial = "message-serial-123",
name = "👍"
)An extension function overload is also available that accepts a Message object directly:
suspend MessageReactions.send(message: Message, name: String, type: MessageReactionType? = null, count: Int = 1): Unit1
2
3
4
room.messages.reactions.send(
message = message,
name = "👍"
)Parameters
The send() method takes the following parameters:
messageSerialrequiredStringnamerequiredStringtypeoptionalMessageReactionTypecountoptionalIntMultiple type.Returns
Unit
This is a suspend function. It completes when the reaction has been sent, or throws a ChatException on failure.
Delete a reaction from a message
suspend reactions.delete(messageSerial: String, name: String? = null, type: MessageReactionType? = null): UnitRemoves a previously sent reaction from a chat message.
This method uses the Ably Chat REST API and does not require the room to be attached.
1
2
3
4
room.messages.reactions.delete(
messageSerial = "message-serial-123",
name = "👍"
)An extension function overload is also available that accepts a Message object directly:
suspend MessageReactions.delete(message: Message, name: String? = null, type: MessageReactionType? = null): Unit1
room.messages.reactions.delete(message = message)Parameters
The delete() method takes the following parameters:
messageSerialrequiredStringnameoptionalStringUnique type.typeoptionalMessageReactionTypeReturns
Unit
This is a suspend function. It completes when the reaction has been deleted, or throws a ChatException on failure.
Subscribe to reaction summaries
reactions.subscribe(listener: MessageReactionListener): SubscriptionSubscribes to aggregated reaction count summaries for messages. Receives updates when the total reaction counts change on any message in the room.
The room must be attached to receive reaction summary events.
1
2
3
4
5
6
7
val subscription = room.messages.reactions.subscribe { event ->
println("Message: ${event.messageSerial}")
println("Reactions: ${event.reactions}")
}
// To stop receiving reaction summaries
subscription.unsubscribe()Parameters
The subscribe() method takes the following parameters:
listenerrequiredMessageReactionSummaryEventReturns
Returns a Subscription object.
Unsubscribe from reaction summaries
subscription.unsubscribe(): UnitCall unsubscribe() to stop receiving reaction summary events.
Subscribe to individual reaction events
reactions.subscribeRaw(listener: MessageRawReactionListener): SubscriptionSubscribes to individual reaction events, receiving notifications each time a user adds or removes a reaction. This provides more granular updates than the summary subscription.
The room must be attached to receive raw reaction events. Raw message reactions must also be enabled when creating the room by setting rawMessageReactions = true in the messages options.
1
2
3
4
5
6
7
8
9
10
11
val subscription = room.messages.reactions.subscribeRaw { event ->
when (event.type) {
MessageReactionEventType.Create ->
println("${event.reaction.clientId} reacted with ${event.reaction.name}")
MessageReactionEventType.Delete ->
println("${event.reaction.clientId} removed ${event.reaction.name}")
}
}
// To stop receiving raw events
subscription.unsubscribe()Parameters
The subscribeRaw() method takes the following parameters:
listenerrequiredMessageReactionRawEventReturns
Returns a Subscription object.
Unsubscribe from raw reaction events
subscription.unsubscribe(): UnitCall unsubscribe() to stop receiving raw reaction events.
Get reactions for a specific client
suspend reactions.clientReactions(messageSerial: String, clientId: String? = null): MessageReactionSummaryRetrieves the reaction data for a specific client on a message. If no client ID is provided, returns reactions for the current user.
1
2
3
4
5
// Get current user's reactions on a message
val myReactions = room.messages.reactions.clientReactions("message-serial-123")
// Get another user's reactions
val theirReactions = room.messages.reactions.clientReactions("message-serial-123", "user-456")Parameters
The clientReactions() method takes the following parameters:
messageSerialrequiredStringclientIdoptionalStringReturns
MessageReactionSummary
This is a suspend function. It returns the reaction data for the specified client, or throws a ChatException on failure.
Collect reaction summaries as a Flow
MessageReactions.asFlow(): Flow<MessageReactionSummaryEvent>Returns a Kotlin Flow that emits reaction summary events. This is an extension function on the MessageReactions interface that provides a reactive alternative to listener-based subscriptions.
1
2
3
4
5
6
7
import kotlinx.coroutines.launch
launch {
room.messages.reactions.asFlow().collect { event ->
println("Message ${event.messageSerial} reactions updated")
}
}Returns
Flow<MessageReactionSummaryEvent>
Returns a Flow that emits MessageReactionSummaryEvent events. The flow automatically manages the underlying subscription.
Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import com.ably.chat.MessageReactionType
import com.ably.chat.MessageReactionEventType
import com.ably.chat.asFlow
import kotlinx.coroutines.launch
val room = chatClient.rooms.get("my-room") {
messages {
rawMessageReactions = true
}
}
room.attach()
// Subscribe to reaction summaries
val summarySub = room.messages.reactions.subscribe { event ->
val reactions = event.reactions
// Update UI with reaction counts
for ((name, summary) in reactions.distinct) {
println("$name: ${summary.total} reactions from ${summary.clientIds.size} clients")
}
}
// Subscribe to individual reaction events for animations
val rawSub = room.messages.reactions.subscribeRaw { event ->
if (event.type == MessageReactionEventType.Create) {
println("${event.reaction.clientId} reacted with ${event.reaction.name}")
}
}
// Or use Flow for reactive collection
launch {
room.messages.reactions.asFlow().collect { event ->
println("Reactions updated for ${event.messageSerial}")
}
}
// Send a distinct reaction (Slack-style)
room.messages.reactions.send(
messageSerial = "message-serial-123",
name = "👍",
type = MessageReactionType.Distinct
)
// Send multiple reactions (Medium-style claps)
room.messages.reactions.send(
messageSerial = "message-serial-456",
name = "👏",
type = MessageReactionType.Multiple,
count = 5
)
// Remove a reaction
room.messages.reactions.delete(
messageSerial = "message-serial-123",
name = "👍"
)
// Check what reactions I've sent on a message
val myReactions = room.messages.reactions.clientReactions("message-serial-123")
println("My reactions: $myReactions")
// Clean up
summarySub.unsubscribe()
rawSub.unsubscribe()