# Reactions

Stream Chat supports message reactions such as likes, hearts, and custom reaction types. Users can react to messages, and reactions can include custom data and scores for cumulative reactions.

## Sending a Reaction

Add a reaction to a message using the `sendReaction` method. Each user can have one reaction of each type per message.

<Tabs>

```js label="JavaScript"
// Add a reaction
const reaction = await channel.sendReaction(messageID, {
  type: "love",
});

// Add a reaction with custom data
const reaction = await channel.sendReaction(messageID, {
  type: "love",
  customField: "value",
});

// Replace all existing reactions from this user with the new one
const reaction = await channel.sendReaction(
  messageID,
  { type: "love" },
  { enforce_unique: true },
);
```

```kotlin label="Kotlin"
val channelClient = client.channel("messaging", "general")

// Add a reaction with custom data
val reaction = Reaction(
  messageId = "message-id",
  type = "like",
  score = 1,
  extraData = mutableMapOf("customField" to "value"),
)
channelClient.sendReaction(reaction).enqueue { result ->
  if (result is Result.Success) {
    val sentReaction: Reaction = result.value
  } else {
    // Handle Result.Failure
  }
}

// Replace all existing reactions from this user with the new one
channelClient.sendReaction(reaction, enforceUnique = true).enqueue { result ->
  // Handle result
}
```

```swift label="Swift"
let channelId = ChannelId(type: .messaging, id: "general")
let messageId = "message-id"
let messageController = chatClient.messageController(cid: channelId, messageId: messageId)

// Add a reaction
messageController.addReaction("like") { error in
  // Handle error
}
```

```dart label="Dart"
// Add a reaction
await channel.sendReaction(messageId, "like");

// Add a reaction with custom data
await channel.sendReaction(messageId, "like", extraData: {"customField": "value"});
```

```java label="Java"
ChannelClient channelClient = client.channel("messaging", "general");

Reaction reaction = new Reaction();
reaction.setMessageId("message-id");
reaction.setType("like");
reaction.setScore(1);
reaction.getExtraData().put("customField", "value");

channelClient.sendReaction(reaction, false).enqueue(result -> {
  if (result.isSuccess()) {
    Reaction sentReaction = result.data();
  } else {
    // Handle result.error()
  }
});
```

```js label="Node.js"
// Server-side: specify user_id
const reaction = await channel.sendReaction(messageID, {
  type: "love",
  user_id: "user-id",
});
```

```python label="Python"
from getstream.models import ReactionRequest

client.chat.send_reaction(
    id=message_id,
    reaction=ReactionRequest(type="love", user_id=user_id, custom={"custom_field": 123}),
)
```

```php label="PHP"
$response = $client->sendReaction("message-id", new Models\SendReactionRequest(
    reaction: new Models\ReactionRequest(
        type: "love",
        userID: $userId,
        custom: (object)["customField" => 123],
    ),
));
```

```ruby label="Ruby"
require 'getstream_ruby'
Models = GetStream::Generated::Models

client.chat.send_reaction(message_id, Models::SendReactionRequest.new(
  reaction: Models::ReactionRequest.new(
    type: 'love',
    user_id: user_id,
    custom: { 'custom_field' => 123 }
  )
))
```

```go label="Go"
resp, err := client.Chat().SendReaction(ctx, msgID, &getstream.SendReactionRequest{
  Reaction: getstream.ReactionRequest{
    Type:   "love",
    UserID: getstream.PtrTo(userID),
    Custom: map[string]any{"custom_field": 123},
  },
})
```

```csharp label="C#"
var resp = await chat.SendReactionAsync(messageId,
    new SendReactionRequest
    {
        Reaction = new ReactionRequest
        {
            Type = "love",
            UserID = userId
        }
    });
```

```csharp label="Unity"
// Send a reaction with default score of 1
await message.SendReactionAsync("like");

// Send a reaction with custom score
await message.SendReactionAsync("clap", 10);

// Replace all previous reactions from this user
await message.SendReactionAsync("love", enforceUnique: true);
```

</Tabs>

### Reaction Parameters

| Name           | Type    | Description                                                              | Default | Optional |
| -------------- | ------- | ------------------------------------------------------------------------ | ------- | -------- |
| message_id     | string  | ID of the message to react to                                            |         |          |
| type           | string  | Reaction type. Each user can have one reaction of each type per message. |         |          |
| score          | integer | Score for cumulative reactions                                           | 1       | ✓        |
| user_id        | string  | User ID (required for server-side calls)                                 |         | ✓        |
| enforce_unique | boolean | If true, replaces all existing reactions from this user with the new one | false   | ✓        |
| skip_push      | boolean | If true, do not send a push notification                                 | false   | ✓        |
| emoji_code     | string  | Unicode emoji for push notification display                              |         | ✓        |
| custom data    | object  | Custom fields for the reaction                                           |         | ✓        |

<admonition type="warning">

Custom data for reactions is limited to 1KB.

</admonition>

## Removing a Reaction

Remove a reaction by specifying the message ID and reaction type.

<Tabs>

```js label="JavaScript"
await channel.deleteReaction(messageID, "love");
```

```kotlin label="Kotlin"
channelClient.deleteReaction(
  messageId = "message-id",
  reactionType = "like",
).enqueue { result ->
  if (result is Result.Success) {
    val message: Message = result.value
  } else {
    // Handle Result.Failure
  }
}
```

```swift label="Swift"
messageController.deleteReaction("like") { error in
  // Handle error
}
```

```dart label="Dart"
await channel.deleteReaction(messageId, "like");
```

```java label="Java"
// Android SDK
channelClient.deleteReaction("message-id", "like").enqueue(result -> {
  if (result.isSuccess()) {
    Message message = result.data();
  } else {
    // Handle result.error()
  }
});

// Backend SDK
chat.deleteReaction(messageId, "like", DeleteReactionRequest.builder()
    .UserID(userId)
    .build()).execute();
```

```python label="Python"
client.chat.delete_reaction(id=message_id, type="love", user_id=user_id)
```

```php label="PHP"
$client->deleteReaction("message-id", "love", $userId);
```

```ruby label="Ruby"
require 'getstream_ruby'
Models = GetStream::Generated::Models

client.chat.delete_reaction(message_id, 'love', user_id)
```

```go label="Go"
_, err := client.Chat().DeleteReaction(ctx, msgID, "love", &getstream.DeleteReactionRequest{
  UserID: getstream.PtrTo(userID),
})
```

```csharp label="C#"
await chat.DeleteReactionAsync(messageId, "love",
    new { user_id = userId });
```

```csharp label="Unity"
await message.DeleteReactionAsync("like");
```

</Tabs>

## Retrieving Reactions

Reactions are included in the message object. Messages returned by the API include the 10 most recent reactions.

### Reaction Fields in Messages

| Field            | Type   | Description                                                                                     |
| ---------------- | ------ | ----------------------------------------------------------------------------------------------- |
| reaction_counts  | object | Count of reactions per type. Example: `{"love": 3, "fire": 2}`                                  |
| reaction_scores  | object | Sum of scores per type. Equals counts for standard reactions; differs for cumulative reactions. |
| reaction_groups  | object | Detailed statistics per type including count, sum_scores, first_reaction_at, last_reaction_at   |
| latest_reactions | array  | The 10 most recent reactions with type, user_id, and created_at                                 |
| own_reactions    | array  | The current user's reactions on this message                                                    |

<disclosure label="Example Reaction Data">

```json
{
  "reaction_counts": {
    "love": 3,
    "fire": 2,
    "thumbsup": 1
  },
  "reaction_scores": {
    "love": 3,
    "fire": 2,
    "thumbsup": 1
  },
  "reaction_groups": {
    "love": {
      "count": 3,
      "sum_scores": 3,
      "first_reaction_at": "2024-12-11T14:32:00.000Z",
      "last_reaction_at": "2024-12-11T15:18:00.000Z"
    },
    "fire": {
      "count": 2,
      "sum_scores": 2,
      "first_reaction_at": "2024-12-11T14:35:00.000Z",
      "last_reaction_at": "2024-12-11T14:52:00.000Z"
    },
    "thumbsup": {
      "count": 1,
      "sum_scores": 1,
      "first_reaction_at": "2024-12-11T16:05:00.000Z",
      "last_reaction_at": "2024-12-11T16:05:00.000Z"
    }
  },
  "latest_reactions": [
    {
      "type": "thumbsup",
      "user_id": "sarah-miller",
      "created_at": "2024-12-11T16:05:00.000Z"
    },
    {
      "type": "love",
      "user_id": "mike-johnson",
      "created_at": "2024-12-11T15:18:00.000Z"
    },
    {
      "type": "fire",
      "user_id": "emma-wilson",
      "created_at": "2024-12-11T14:52:00.000Z"
    }
  ],
  "own_reactions": []
}
```

</disclosure>

<admonition type="info">

Use `reaction_groups` instead of `reaction_counts` for if you're building a custom implementation. The `reaction_groups` field provides additional metadata including timestamps and is the recommended approach.

</admonition>

To retrieve more than 10 reactions, use pagination.

### Paginating Reactions

Retrieve reactions with pagination using `limit` and `offset` parameters.

| Parameter | Maximum Value |
| --------- | ------------- |
| limit     | 300           |
| offset    | 1000          |

<Tabs>

```js label="JavaScript"
// Get the first 10 reactions
const response = await channel.getReactions(messageID, { limit: 10 });

// Get reactions 11-13
const response = await channel.getReactions(messageID, {
  limit: 3,
  offset: 10,
});
```

```kotlin label="Kotlin"
// Get the first 10 reactions
channelClient.getReactions(
  messageId = "message-id",
  offset = 0,
  limit = 10,
).enqueue { result ->
  if (result is Result.Success) {
    val reactions: List<Reaction> = result.value
  } else {
    // Handle Result.Failure
  }
}

// Get reactions 11-20
channelClient.getReactions(
  messageId = "message-id",
  offset = 10,
  limit = 10,
).enqueue { /* ... */ }

// Get reactions after a specific reaction ID
channelClient.getReactions(
  messageId = "message-id",
  firstReactionId = "reaction-id",
  limit = 10,
).enqueue { /* ... */ }
```

```swift label="Swift"
// Load first 10 reactions
messageController.loadReactions(limit: 10, offset: 0) { result in
  let loadedReactions = messageController.reactions
}

// Load next 10 reactions
messageController.loadNextReactions(limit: 10) { error in
  let loadedReactions = messageController.reactions
}
```

```dart label="Dart"
// Get the first 10 reactions
await channel.getReactions(messageId, PaginationParams(limit: 10));

// Get reactions 11-13
await channel.getReactions(messageId, PaginationParams(limit: 3, offset: 10));
```

```java label="Java"
// Android SDK
int offset = 0;
int limit = 10;

// Get the first 10 reactions
channelClient.getReactions("message-id", offset, limit).enqueue(result -> {
  if (result.isSuccess()) {
    List<Reaction> reactions = result.data();
  } else {
    // Handle result.error()
  }
});

// Get reactions 11-20
channelClient.getReactions("message-id", 10, limit).enqueue(result -> { /* ... */ });

// Backend SDK
chat.getReactions(messageId, GetReactionsRequest.builder()
    .Limit(10).Offset(0).build()).execute();
```

```python label="Python"
# Get the first 10 reactions
client.chat.get_reactions(id=message_id, limit=10)

# Get reactions 11-13
client.chat.get_reactions(id=message_id, limit=3, offset=10)
```

```php label="PHP"
// Get the first 10 reactions
$response = $client->getReactions("message-id", 10, 0);

// Get reactions 11-13
$response = $client->getReactions("message-id", 3, 10);
```

```ruby label="Ruby"
require 'getstream_ruby'
Models = GetStream::Generated::Models

# Get the first 10 reactions
client.chat.get_reactions(message_id, 10)

# Get reactions 11-13
client.chat.get_reactions(message_id, 3, 10)
```

```go label="Go"
// Get the first 10 reactions
resp, err := client.Chat().GetReactions(ctx, msgID, &getstream.GetReactionsRequest{
  Limit: getstream.PtrTo(10),
})
```

```csharp label="C#"
// Get the first 10 reactions
var resp = await chat.GetReactionsAsync(messageId, new { limit = 10 });

// Get reactions 11-13
var nextResp = await chat.GetReactionsAsync(messageId, new { limit = 3, offset = 10 });
```

</Tabs>

## Querying Reactions

Filter reactions by type or user on a specific message. This endpoint requires the user to have read permission on the channel when called client-side.

<Tabs>

```js label="JavaScript"
// Query reactions by type
await client.queryReactions(message.id, { type: "like" });

// Query reactions by user
await client.queryReactions(message.id, { user_id: userId });

// Paginate results
const firstPage = await client.queryReactions(message.id, {});
const secondPage = await client.queryReactions(
  message.id,
  {},
  {},
  { limit: 5, next: firstPage.next },
);
```

</Tabs>

## Cumulative Reactions

Cumulative reactions allow users to react multiple times to the same message, with the total score tracked. This is useful for features like Medium's "clap" functionality.

Set a `score` value when sending the reaction. The API returns:

- `sum_scores`: Total score across all users
- Individual user scores

<Tabs>

```js label="JavaScript"
// User claps 5 times
await channel.sendReaction(messageID, {
  type: "clap",
  score: 5,
});

// Same user claps 20 more times (total becomes 25)
await channel.sendReaction(messageID, {
  type: "clap",
  score: 25,
});
```

```kotlin label="Kotlin"
val reaction = Reaction(messageId = "message-id", type = "clap", score = 5)
channelClient.sendReaction(reaction).enqueue { /* ... */ }
```

```swift label="Swift"
messageController.addReaction("clap", score: 5) { error in
  // Handle error
}
```

```dart label="Dart"
// User claps 5 times
await channel.sendReaction(messageId, "clap", score: 5);

// Same user claps 20 more times
await channel.sendReaction(messageId, "clap", score: 25);
```

```java label="Java"
// Android SDK
Reaction reaction = new Reaction();
reaction.setMessageId("message-id");
reaction.setType("clap");
reaction.setScore(5);

channelClient.sendReaction(reaction, false).enqueue(result -> { /* ... */ });

// Backend SDK
chat.sendReaction(messageId, SendReactionRequest.builder()
    .reaction(ReactionRequest.builder()
        .type("clap")
        .score(5)
        .userID(userId)
        .build())
    .build()).execute();
```

```python label="Python"
from getstream.models import ReactionRequest

# User claps 5 times
client.chat.send_reaction(
    id=message_id,
    reaction=ReactionRequest(type="clap", score=5, user_id=user_id),
)

# Same user claps 20 more times
client.chat.send_reaction(
    id=message_id,
    reaction=ReactionRequest(type="clap", score=25, user_id=user_id),
)
```

```php label="PHP"
// User claps 5 times
$client->sendReaction("message-id", new Models\SendReactionRequest(
    reaction: new Models\ReactionRequest(type: "clap", score: 5, userID: $userId),
));

// Same user claps 20 more times
$client->sendReaction("message-id", new Models\SendReactionRequest(
    reaction: new Models\ReactionRequest(type: "clap", score: 25, userID: $userId),
));
```

```ruby label="Ruby"
require 'getstream_ruby'
Models = GetStream::Generated::Models

# User claps 5 times
client.chat.send_reaction(message_id, Models::SendReactionRequest.new(
  reaction: Models::ReactionRequest.new(
    type: 'clap',
    score: 5,
    user_id: user_id
  )
))

# Same user claps 20 more times
client.chat.send_reaction(message_id, Models::SendReactionRequest.new(
  reaction: Models::ReactionRequest.new(
    type: 'clap',
    score: 25,
    user_id: user_id
  )
))
```

```go label="Go"
_, err = client.Chat().SendReaction(ctx, msgID, &getstream.SendReactionRequest{
  Reaction: getstream.ReactionRequest{
    Type:   "clap",
    Score:  getstream.PtrTo(25),
    UserID: getstream.PtrTo(userID),
  },
})
```

```csharp label="C#"
var resp = await chat.SendReactionAsync(messageId,
    new SendReactionRequest
    {
        Reaction = new ReactionRequest
        {
            Type = "clap",
            UserID = userId,
            Score = 5
        }
    });
```

```csharp label="Unity"
await message.SendReactionAsync("clap", score: 5);
```

</Tabs>


---

This page was last updated at 2026-05-22T16:32:21.418Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/unity/send-reaction/](https://getstream.io/chat/docs/unity/send-reaction/).