import { Text } from "react-native";
import { ReactionData } from "stream-chat-react-native";
const supportedReactions: ReactionData[] = [
{ type: "happy", Icon: () => <Text>😄</Text> },
{ type: "sad", Icon: () => <Text>😦</Text> },
{ type: "love", Icon: () => <Text>❤️</Text> },
{ type: "angry", Icon: () => <Text>😠</Text> },
{ type: "haha", Icon: () => <Text>😆</Text> },
];
<Channel supportedReactions={supportedReactions}>
{/* Underlying MessageList and MessageInput components */}
</Channel>;Customize Message Reactions
Message reactions are a key part of chat apps. The SDK includes built-in support.
Best Practices
- Keep reaction sets small and familiar to reduce UI clutter.
- Use
supportedReactionsto keep server and client reaction types aligned. - Choose a single
reactionListPositionto avoid shifting layouts. - Respect
enforceUniqueReactionto simplify downstream analytics. - Reuse
useMessageContexthandlers to keep overlay and press behavior consistent.
Supported Reaction types
You can customize the supported types of reactions for your application using the supportedReactions prop of the Channel component.
It expects an array of objects with Icon and type. Use ReactionData for type safety.
Example:
Reaction List Position
Reactions can render at the top or bottom using reactionListPosition on Channel.
<Channel reactionListPosition="bottom">
{/* Underlying MessageList and MessageInput components */}
</Channel>
|
|
| Top Reactions | Bottom Reactions |
Enforce unique reactions
You can enforce a single reaction per user per message.
To do this, you can enable the enforceUniqueReaction prop in the Channel component.
<Channel enforceUniqueReaction>
{/* Underlying MessageList and MessageInput components */}
</Channel>Customizing message Reaction List components
When a message has reactions, you can customize:
ReactionListTopReactionListBottom
Pass custom UI via Channel props:

import { Pressable, StyleSheet } from "react-native";
import { Channel, useMessageContext } from "stream-chat-react-native";
const CustomReactionListBottomComponent = () => {
const {
handleReaction,
onPress,
onLongPress,
reactions,
showMessageOverlay,
} = useMessageContext();
return (
<View style={styles.container}>
{reactions?.map((reaction) => {
const { type, Icon } = reaction;
return (
<Pressable
onLongPress={(event) => {
if (onLongPress) {
onLongPress({
defaultHandler: () => {
if (handleReaction) {
showMessageOverlay(true);
}
},
emitter: "reactionList",
event,
});
}
}}
onPress={(event) => {
onPress({
defaultHandler: () => {
if (handleReaction) {
handleReaction(reaction.type);
}
},
emitter: "reactionList",
event,
});
}}
key={type}
>
{Icon ? <Icon /> : null}
</Pressable>
);
})}
</View>
);
};
const ChannelScreen = () => {
return (
<Channel
reactionListPosition="bottom"
ReactionListBottom={CustomReactionListBottomComponent}
>
{/* Underlying MessageList and MessageInput components */}
</Channel>
);
};
const styles = StyleSheet.create({
container: {
borderRadius: 4,
flexDirection: "row",
marginVertical: 2,
},
});Customizing Reaction Picker component
Customize the reaction picker via MessageReactionPicker on Channel.

import { FlatList, Pressable, StyleSheet } from "react-native";
import {
Channel,
useMessageContext,
useMessagesContext,
} from "stream-chat-react-native";
const CustomMessageReactionPicker = () => {
const { supportedReactions } = useMessagesContext();
const { dismissOverlay, handleReaction } = useMessageContext();
const renderItem = ({ item }: { item: ReactionData }) => {
return (
<Pressable
key={item.type}
onPress={() => {
if (handleReaction) {
handleReaction(item.type);
}
dismissOverlay();
}}
>
{item.Icon ? <item.Icon /> : null}
</Pressable>
);
};
return (
<FlatList
columnWrapperStyle={styles.container}
data={supportedReactions}
keyExtractor={(item) => item.type}
numColumns={6}
renderItem={renderItem}
/>
);
};
const ChannelScreen = () => {
return (
<Channel
reactionListPosition="bottom"
MessageReactionPicker={CustomMessageReactionPicker}
>
{/* Underlying MessageList and MessageInput components */}
</Channel>
);
};
const styles = StyleSheet.create({
container: {
justifyContent: "space-evenly",
marginVertical: 8,
},
});Customizing Message Reactions by user list component
We display the list of the reactions reacted by users in the channel in the MessageUserReactions component and each of the item in the list using the MessageUserReactionsItem and the avatar using the MessageUserReactionsAvatar component.

import { useMemo } from "react";
import { Image, FlatList, StyleSheet, Text, View } from "react-native";
import { ReactionSortBase } from "stream-chat";
import {
Channel,
Reaction,
useFetchReactions,
useMessageContext,
} from "stream-chat-react-native";
const CustomMessageUserReactions = () => {
const { message } = useMessageContext();
const reactionSort: ReactionSortBase = {
created_at: -1,
};
const { reactions: fetchedReactions } = useFetchReactions({
message,
sort: reactionSort,
});
const reactions = useMemo(
() =>
fetchedReactions.map((reaction) => ({
id: reaction.user?.id,
image: reaction.user?.image,
name: reaction.user?.name,
type: reaction.type,
})) as Reaction[],
[fetchedReactions],
);
const renderItem = ({ item }: { item: Reaction }) => {
const Icon = supportedReactions?.find(
(supportedReaction) => supportedReaction.type === item.type,
)?.Icon;
return (
<View style={styles.container}>
<MessageUserReactionsAvatar reaction={item} />
<Text style={styles.text}>{item.name}</Text>
<View style={styles.icon}>{Icon && <Icon />}</View>
</View>
);
};
return (
<FlatList
data={reactions}
keyExtractor={(item) => item.id}
renderItem={renderItem}
/>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: "row",
flexWrap: "wrap",
marginHorizontal: 16,
alignItems: "center",
marginVertical: 8,
},
icon: {
position: "absolute",
right: 0,
},
text: {
marginHorizontal: 16,
},
});Similarly, you can customize the MessageUserReactionsItem and MessageUserReactionsAvatar component.

