Reactions

LAST EDIT Apr 08 2021

Stream Chat has built-in support for user Reactions. Common examples are likes, comments, loves, etc. Reactions can be customized so that you are able to use any type of reaction your application requires.

Similar to other objects in Stream Chat, reactions allow you to add custom data to the reaction of your choice. This is helpful if you want to customize the reaction logic.

nametypedescriptiondefaultoptional
message_idstringID of the message to react to
reactionobjectReaction object
reaction.typestringType of the reaction. User could have only 1 reaction of each type per message
reaction.scoreintegerScore of the reaction for cumulative reactions (see example below)1
user_idstringUser ID for server side calls
enforce_uniquebooleanIf set to true, new reaction will replace all reactions the user has (if any) on this messagefalse
Custom data for reactions is limited to 1KB.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Add reaction 'love' with custom field 
const reaction = await channel.sendReaction(messageID, { 
    type: 'love', 
    myCustomField: 123 
}); 
 
// Add reaction 'love' from the server side 
const reaction = await channel.sendReaction(messageID, { 
    type: 'love' 
}, userID); 
 
// Add reaction 'like' and replace all other reactions of this user by it 
const reaction = await channel.sendReaction(messageID, { 
    type: 'like' 
}, null, true);
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
ChannelClient channelClient = client.channel("messaging", "general"); 
 
// Add reaction 'like' with a custom field 
Reaction reaction = new Reaction(); 
reaction.setMessageId("message-id"); 
reaction.setType("like"); 
reaction.setScore(1); 
reaction.getExtraData().put("customField", 1); 
 
boolean enforceUnique = false; // Don't remove other existing reactions 
channelClient.sendReaction(reaction, enforceUnique).enqueue(result -> { 
    if (result.isSuccess()) { 
        Reaction sentReaction = result.data(); 
    } else { 
        // Handle result.error() 
    } 
}); 
 
// Add reaction 'like' and replace all other reactions of this user by it 
enforceUnique = true; 
channelClient.sendReaction(reaction, enforceUnique).enqueue(result -> { 
    if (result.isSuccess()) { 
        Reaction sentReaction = result.data(); 
    } else { 
        // Handle result.error() 
    } 
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import StreamChat 
 
/// 1: Create a `ChannelId` that represents the channel you want to get a message from. 
let channelId = ChannelId(type: .messaging, id: "general") 
 
/// 2: Create a `MessageId` that represents the message you want to get. 
let messageId = "message-id" 
 
/// 3: Use the `ChatClient` to create a `ChatMessageController` with the `ChannelId` and message id. 
let messageController = chatClient.messageController(cid: channelId, messageId: messageId) 
 
/// 4: Call `ChatMessageController.addReaction` to add the reaction. 
messageController.addReaction("like") { error in 
    print(error ?? "message liked") 
}
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
val channelClient = client.channel("messaging", "general") 
 
// Add reaction 'like' with a custom field 
val reaction = Reaction( 
    messageId = "message-id", 
    type = "like", 
    score = 1, 
    extraData = mutableMapOf("customField" to 1), 
) 
channelClient.sendReaction(reaction).enqueue { result -> 
    if (result.isSuccess) { 
        val sentReaction: Reaction = result.data() 
    } else { 
        // Handle result.error() 
    } 
} 
 
// Add reaction 'like' and replace all other reactions of this user by it 
channelClient.sendReaction(reaction, enforceUnique = true).enqueue { result -> 
    if (result.isSuccess) { 
        val sentReaction = result.data() 
    } else { 
        // Handle result.error() 
    } 
}
1
$response = $channel->sendReaction('message-id', ['type'=>'love','myCustomField'=>123], $userId);
1
await channel.sendReaction("messageID", "like", extraData: {"customField": 1});
1
channel.send_reaction(message_id, {"type": "love", "my_custom_field": 123}, user_id)
1
2
3
4
5
reaction := &stream_chat.Reaction{ 
	Type:      "love", 
	ExtraData: map[string]interface{}{"my_custom_field": 123}, 
} 
msg, err := channel.SendReaction(reaction, msgID, userID)

Removing a Reaction

Copied!
1
await channel.deleteReaction(messageID, 'love');
1
2
3
4
5
6
7
8
String reactionType = "like"; 
channelClient.deleteReaction("message-id", reactionType).enqueue(result -> { 
    if (result.isSuccess()) { 
        Message message = result.data(); 
    } else { 
        // Handle result.error() 
    } 
});
1
2
3
messageController.deleteReaction("like") { error in 
    print(error ?? "like removed") 
}
1
2
3
4
5
6
7
8
9
10
channelClient.deleteReaction( 
    messageId = "message-id", 
    reactionType = "like", 
).enqueue { result -> 
    if (result.isSuccess) { 
        val message = result.data() 
    } else { 
        // Handle result.error() 
    } 
}
1
$response = $channel->deleteReaction('message-id','love', $userId);
1
await channel.deleteReaction("messageID", "like");
1
channel.delete_reaction(message_id, "love", user_id)
1
_, err := channel.DeleteReaction(msgID, "love", userID)

Paginating Reactions

Copied!

Messages returned by the APIs automatically include the 10 most recent reactions. You can also retrieve more reactions and paginate using the following logic:

1
2
3
4
5
// get the first 10 reactions 
const response = await channel.getReactions(messageID, { limit: 10 }); 
 
// get 3 reactions past the first 10 
const response = await channel.getReactions(messageID, { limit: 3, offset: 10 });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Get the first 10 reactions 
int offset = 0; 
int limit = 10; 
channelClient.getReactions("message-id", offset, limit).enqueue(result -> { 
    if (result.isSuccess()) { 
        List<Reaction> reactions = result.data(); 
    } else { 
        // Handle result.error() 
    } 
}); 
 
// Get the second 10 reactions 
offset = 10; 
channelClient.getReactions("message-id", offset, limit) 
        .enqueue(result -> { /* ... */ }); 
 
// Get 10 reactions after particular reaction 
String reactionId = "reaction-id"; 
channelClient.getReactions("message-id", reactionId, limit) 
        .enqueue(result -> { /* ... */ });
1
// Swift SDK currently doesn't support reaction pagination
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
// Get the first 10 reactions 
channelClient.getReactions( 
    messageId = "message-id", 
    offset = 0, 
    limit = 10, 
).enqueue { result -> 
    if (result.isSuccess) { 
        val reactions: List<Reaction> = result.data() 
    } else { 
        // Handle result.error() 
    } 
} 
 
// Get the second 10 reactions 
channelClient.getReactions( 
    messageId = "message-id", 
    offset = 10, 
    limit = 10, 
).enqueue { /* ... */ } 
 
// Get 10 reactions after particular reaction 
channelClient.getReactions( 
    messageId = "message-id", 
    firstReactionId = "reaction-id", 
    limit = 10, 
).enqueue { /* ... */ }
1
2
3
4
5
// get the first 10 reactions 
$response = $channel->getReactions('message-id',['limit' => 10]); 
 
// get 3 reactions past the first 10 
$response = $channel->getReactions('message-id',['limit' => 3, 'offset' => 10]);
1
2
3
4
5
// get the first 10 reactions 
await channel.getReactions("messageID", PaginationParams(limit: 10)); 
 
// get 3 reactions past the first 10 
await channel.getReactions("messageID", PaginationParams(limit: 3, offset:10));
1
2
3
4
5
# get the first 10 reactions 
channel.get_reactions(message_id) 
 
# get 3 reactions past the first 10 
channel.get_reactions(message_id, limit=3, offset=10)
1
2
3
4
5
6
options := map[string][]string{ 
		"limit": {"10"}, 
		"idlte": {"10"}, 
	} 
 
	_, err := channel.GetReactions(msgID, options)

Cumulative (Clap) Reactions

Copied!

You can use the Reactions API to build something similar to Medium's clap reactions. If you are not familiar with this, Medium allows you to clap articles more than once and shows the sum of all claps from all users.

To do this, you only need to include a score for the reaction (ie. user X clapped 25 times) and the API will return the sum of all reaction scores as well as each user individual scores (ie. clapped 475 times, user Y clapped 14 times).

1
2
3
4
5
6
7
8
9
10
11
// user claps 5 times on a message 
await channel.sendReaction(messageID, { 
    type: 'clap', 
    score: 5, 
}); 
 
// same user claps 20 times more 
await channel.sendReaction(messageID, { 
    type: 'clap', 
   score: 25, 
});
1
2
3
4
5
// user claps 5 times on a message 
await channel.sendReaction("messageID", "like", score: 5); 
 
// same user claps 20 times more 
await channel.sendReaction("messageID", "like", score: 25);
1
2
3
4
5
# user claps 5 times on a message 
channel.send_reaction(message_id, {"type": "clap", "score": 5}, user_id) 
 
# same user claps 20 times more 
channel.send_reaction(message_id, {"type": "clap", "score": 25}, user_id)
1
2
3
4
5
6
7
Reaction reaction = new Reaction(); 
reaction.setMessageId("message-id"); 
reaction.setType("like"); 
reaction.setScore(5); 
 
boolean enforceUnique = false; 
channelClient.sendReaction(reaction, enforceUnique).enqueue(result -> { /* ... */ });
1
2
val reaction = Reaction(messageId = "message-id", type = "clap", score = 5) 
channelClient.sendReaction(reaction).enqueue { /* ... */ }
1
2
3
4
5
// user claps 5 times on a message 
$channel->sendReaction('message-id', ['type'=>'clap', 'score'=>5], $userId); 
 
// same user claps 20 times more 
$channel->sendReaction('message-id', ['type'=>'clap', 'score'=>25], $userId);
1
2
3
_, err = channel.SendReaction(&stream_chat.Reaction{ 
	Type: "clap", ExtraData: map[string]interface{}{"score": 25}, 
}, msgID, userID)
1
2
3
messageController.addReaction("like", score: 2) { error in 
    print(error ?? "message liked twice") 
}