Reaction components
Reactions show in three places: on the message, as a list above or under the bubble; in the message menu, as a picker above the message; and in a bottom sheet that lists who reacted. Every component on this page is an override key of the same name. The ReactionListItemWrapper key has no effect; see ReactionListItemWrapper. Reactions covers the feature across the layers.
Best practices
- Set the reactions on offer with
supportedReactionsonChannelorThread. Every component here reads them fromMessagesContext, and a reaction of a type not in the list doesn't show on messages. - Pick the layout with
reactionListPositionandreactionListTypeonChannelbefore you replace a component. - Change what a tap on a reaction does with
onPressMessageonChannel: the payload'semitterisreactionListandadditionalInfo.reactionTypethe pressed type. Toggling a reaction with a tap shows it. - Change what picking a reaction does with
selectReactionorhandleReactiononChannel, not by replacing the picker.
Basic usage
This wrapper shows the reactions above the bubble as separate pills without counts:
import {
ReactionListTop,
type ReactionListTopProps,
WithComponents,
} from "stream-chat-react-native";
const ReactionListTopWithoutCounts = (props: ReactionListTopProps) => (
<ReactionListTop {...props} showCount={false} type="segmented" />
);
const overrides = { ReactionListTop: ReactionListTopWithoutCounts };
export const MessagesWithPlainReactions = ({
children,
}: {
children: React.ReactNode;
}) => <WithComponents overrides={overrides}>{children}</WithComponents>;Custom reactions changes the reactions on offer and their icons.
The reactions on a message
MessageItemView renders ReactionListTop above the bubble with reactionListPosition="top", the default, and ReactionListBottom under it with "bottom", passing reactionListType as type. Both render nothing while the message has no reaction of a supported type. A message's reactions come from MessageContext.reactions: one entry for each type, with its count and whether the user gave it, the oldest type first.
Pressing a reaction reports the reactionList emitter, and by default opens the reactions sheet with that type selected. Pressing the clustered list, or the "+N" pill, opens it with every type.
ReactionListTop
With type="clustered" it renders ReactionListClustered. With any other type, or none, it renders up to four ReactionListItem pills in a row, then a ReactionListCountItem with the number of other types. A prop set here is used when it's truthy; otherwise the context value is.
| Prop | Type | Default | Description |
|---|---|---|---|
alignment |
Alignment |
from MessageContext |
Puts the list at the bubble's right or left edge. |
hasReactions |
boolean |
from MessageContext |
Whether the message has reactions. |
onLongPress |
(payload: PressableHandlerPayload) => void |
from MessageContext |
Called on a long press on a pill. |
onPress |
(payload: MessagePressableHandlerPayload) => void |
from MessageContext |
Called on a press on a pill. |
onPressIn |
((payload: PressableHandlerPayload) => void) | null |
from MessageContext |
Called when a press on a pill starts. |
preventPress |
boolean |
from MessageContext |
Turns off presses. |
reactionListType |
"clustered" | "segmented" |
Not used. The list follows type. |
|
reactions |
ReactionSummary[] |
from MessageContext |
The reactions to show. |
showCount |
boolean |
true |
Shows each pill's count. |
showReactionsOverlay |
(selectedReaction?: string) => void |
from MessageContext |
Opens the reactions sheet. |
supportedReactions |
ReactionData[] |
from MessagesContext |
The reactions on offer, for the icons. |
type |
"clustered" | "segmented" |
'clustered' renders ReactionListClustered. |
ReactionListBottom
With type="segmented" it renders a ReactionListItem pill for every type, five to a row, or six without counts. With any other type, or none, it renders ReactionListClustered. A prop set here is used when it's truthy; otherwise the context value is.
| Prop | Type | Default | Description |
|---|---|---|---|
alignment |
Alignment |
from MessageContext |
Aligns the rows left or right. |
hasReactions |
boolean |
from MessageContext |
Whether the message has reactions. |
onLongPress |
(payload: PressableHandlerPayload) => void |
from MessageContext |
Called on a long press on a pill. |
onPress |
(payload: MessagePressableHandlerPayload) => void |
from MessageContext |
Called on a press on a pill. |
onPressIn |
((payload: PressableHandlerPayload) => void) | null |
from MessageContext |
Called when a press on a pill starts. |
preventPress |
boolean |
from MessageContext |
Turns off presses. |
reactions |
ReactionSummary[] |
from MessageContext |
The reactions to show. |
showCount |
boolean |
true |
Shows each pill's count. |
showReactionsOverlay |
(selectedReaction?: string) => void |
from MessageContext |
Opens the reactions sheet. |
supportedReactions |
ReactionData[] |
from MessagesContext |
The reactions on offer, for the icons. |
type |
"clustered" | "segmented" |
'segmented' renders a pill for each type. |
ReactionListClustered
Renders the icons of up to four reaction types in one pill, followed by the number of other types when there are more. Pressing it reports the reactionList emitter, and by default opens the reactions sheet with every type.
| Prop | Type | Default | Description |
|---|---|---|---|
accessibilityLabel |
string |
The pill's accessibility label. | |
containerStyle |
StyleProp<ViewStyle> |
Style of the pill. | |
hasReactions |
boolean |
from MessageContext |
Whether the message has reactions. |
onPress |
(payload: MessagePressableHandlerPayload) => void |
from MessageContext |
Called on a press. |
onPressIn |
((payload: PressableHandlerPayload) => void) | null |
from MessageContext |
Called when a press starts. |
preventPress |
boolean |
from MessageContext |
Turns off presses. |
reactions |
ReactionSummary[] |
from MessageContext |
The reactions to show. |
showReactionsOverlay |
(selectedReaction?: string) => void |
from MessageContext |
Opens the reactions sheet. |
supportedReactions |
ReactionData[] |
from MessagesContext |
The reactions on offer, for the icons. |
ReactionListItem
Renders one reaction type as a pill: its icon, or the Unknown icon when the type isn't in supportedReactions, and its count. A press or a long press reports the reactionList emitter with additionalInfo.reactionType, and by default opens the reactions sheet at that type.
| Prop | Type | Default | Description |
|---|---|---|---|
onLongPress |
(payload: PressableHandlerPayload) => void |
Called on a long press. | |
onPress |
(payload: MessagePressableHandlerPayload) => void |
Called on a press. | |
onPressIn |
((payload: PressableHandlerPayload) => void) | null |
Called when a press starts. | |
preventPress |
boolean |
Turns off presses. | |
reaction |
ReactionSummary |
required | The reaction type, with its count. |
selected |
boolean |
false |
Highlights the pill, and tells screen readers the user reacted. The lists pass the reaction's own. |
showCount |
boolean |
true |
Shows the count. |
showReactionsOverlay |
(selectedReaction?: string) => void |
Opens the reactions sheet. | |
supportedReactions |
ReactionData[] |
The reactions on offer, for the icon. |
ReactionListItem reads nothing from context: the lists pass every prop.
ReactionListCountItem
Renders "+3", the number of reaction types ReactionListTop leaves out. It renders nothing for a count of 0. Pressing it opens the reactions sheet with every type.
| Prop | Type | Default | Description |
|---|---|---|---|
count |
number |
required | The number to show. |
onLongPress |
(payload: PressableHandlerPayload) => void |
Called on a long press. | |
onPress |
(payload: MessagePressableHandlerPayload) => void |
Called on a press. | |
onPressIn |
((payload: PressableHandlerPayload) => void) | null |
Called when a press starts. | |
preventPress |
boolean |
Turns off presses. | |
selected |
boolean |
Highlights the pill. | |
showReactionsOverlay |
(selectedReaction?: string) => void |
Opens the reactions sheet. |
ReactionListItemWrapper
The Pressable pill the items and the clustered list render in. They import it directly, so the ReactionListItemWrapper override key has no effect; style the pill through theme.messageItemView.reactionListItemWrapper. It takes every Pressable prop, and:
| Prop | Type | Default | Description |
|---|---|---|---|
selected |
boolean |
false |
Uses the selected background. |
style |
StyleProp<ViewStyle> |
Style of the pill, applied after the SDK's. |
The reaction picker
MessageReactionPicker renders above the message while the message menu is open: the reactions of supportedReactions with isMain, the ones the user gave highlighted, and a button that opens a sheet with the full emoji list. Picking a reaction closes the menu, then calls handleReaction. It renders nothing when the user can't send reactions.
| Prop | Type | Default | Description |
|---|---|---|---|
dismissOverlay |
() => void |
required | Closes the message menu. |
handleReaction |
(reactionType: string) => Promise<void> |
Called with the picked type after the menu closes. Message passes the message's handleReaction when the user can send reactions. |
|
supportedReactions |
ReactionData[] |
Not used. The picker reads them from MessagesContext. |
The emoji sheet lists a fixed set of emojis, whatever supportedReactions holds.
The reactions sheet
MessageUserReactions renders in a bottom sheet, which showReactionsOverlay opens: the total count, a row of the message's reaction types with their counts, and the users who reacted, loaded page by page, newest first. Picking a type in the row filters the users. The + button at the start of the row shows the emoji list; picking an emoji calls the message's handleReaction and closes the sheet.
| Prop | Type | Default | Description |
|---|---|---|---|
message |
LocalMessage |
The message whose reactions show. Message passes it; it isn't read from context. |
|
reactions |
Reaction[] |
the reactions loaded from the server | The users to list, in place of the loaded ones. |
selectedReaction |
string |
The type selected when the sheet opens. | |
supportedReactions |
ReactionData[] |
from MessagesContext |
The reactions on offer. Types not in it don't show in the row. |
MessageUserReactionsItem
Renders one user in the sheet, with the MessageUserReactionsAvatar override, the name, or "You" with "Tap to remove", and the reaction's icon. Pressing the user's own reaction removes it through handleReaction. It isn't exported; replace it with the MessageUserReactionsItem key.
| Prop | Type | Default | Description |
|---|---|---|---|
reaction |
Reaction |
required | The reaction: the user's id, name and image, and the type. |
supportedReactions |
ReactionData[] |
required | The reactions on offer, for the icon. |
MessageUserReactionsAvatar
Renders the avatar of the user who reacted, or their initials.
| Prop | Type | Default | Description |
|---|---|---|---|
reaction |
Reaction |
required | The reaction, with the user's name and image. |
size |
"sm" | "md" | "lg" | "xl" | "xs" | "2xl" |
'lg' |
The avatar's size. MessageUserReactionsItem passes 'md'. |
Related hooks
useMessageContext()returns the message'sreactions,hasReactions,handleReaction,handleToggleReactionandshowReactionsOverlay.useMessagesContext()returnssupportedReactions,reactionListPositionandreactionListType.useFetchReactions({ message, reactionType, sort })loads the users who reacted, as the sheet does. See Message hooks.