MessageReactions

Open in

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.

Kotlin

1

val reactions = room.messages.reactions

Message 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): Unit

Sends 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.

Kotlin

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): Unit
Kotlin

1

2

3

4

room.messages.reactions.send(
    message = message,
    name = "👍"
)

Parameters

The send() method takes the following parameters:

messageSerialrequiredString
The serial of the message to react to.
namerequiredString
The reaction identifier, typically an emoji.
typeoptionalMessageReactionType
The counting strategy for this reaction. Defaults vary by room configuration.
countoptionalInt
Number of times to count this reaction. Only applies to Multiple 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): Unit

Removes 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.

Kotlin

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): Unit
Kotlin

1

room.messages.reactions.delete(message = message)

Parameters

The delete() method takes the following parameters:

messageSerialrequiredString
The serial of the message to remove the reaction from.
nameoptionalString
The reaction identifier to delete. Required except for Unique type.
typeoptionalMessageReactionType
The counting strategy of the reaction to delete.

Returns

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): Subscription

Subscribes 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.

Kotlin

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:

listenerrequiredMessageReactionSummaryEvent
Callback invoked when reaction summaries change.

Returns

Subscription

Returns a Subscription object.

Unsubscribe from reaction summaries

subscription.unsubscribe(): Unit

Call unsubscribe() to stop receiving reaction summary events.

Subscribe to individual reaction events

reactions.subscribeRaw(listener: MessageRawReactionListener): Subscription

Subscribes 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.

Kotlin

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:

listenerrequiredMessageReactionRawEvent
Callback invoked for each individual reaction event.

Returns

Subscription

Returns a Subscription object.

Unsubscribe from raw reaction events

subscription.unsubscribe(): Unit

Call unsubscribe() to stop receiving raw reaction events.

Get reactions for a specific client

suspend reactions.clientReactions(messageSerial: String, clientId: String? = null): MessageReactionSummary

Retrieves the reaction data for a specific client on a message. If no client ID is provided, returns reactions for the current user.

Kotlin

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:

messageSerialrequiredString
The serial of the message to get reactions for.
clientIdoptionalString
The client ID to get reactions for. Defaults to the current user.

Returns

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.

Kotlin

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

Kotlin

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