Issue
When viewing a channel at (or near) the bottom of the message list, adding a
reaction (e.g. a "like"/love) to a recent message causes the MessageList to
unexpectedly scroll upward, away from the message the user just reacted to. A
common trigger: a new message arrives, the user taps to "like" it, and the list
immediately jumps up (it appears to land around older/unread messages).
The reacting interaction itself uses the SDK's default reaction handler - there
is no custom reaction send/toggle logic. The list is a standard <MessageList />
inside <Channel>.
This appears to be the React Native counterpart of two bugs already reported and
fixed in the sibling SDKs:
Steps to reproduce
Steps to reproduce the behavior:
- Open a channel that has enough messages to be scrollable.
- Scroll to the bottom (latest messages).
- Send several new messages to the channel from another user while the recipient stays in the chatroom.
- Recipient adds a reaction to the most recent messages (long-press the message → tap the reaction).
- See the list scroll/jump upward instead of staying anchored on the reacted
message.
Expected behavior
Adding a reaction should not move the scroll position - the list should stay
anchored on the message the user reacted to (the behavior restored by the Android
fix #5280).
Project Related Information
Customization
Click To Expand
The bug reproduces with a standard MessageList. Our Channel configures a
single custom reaction and a custom MessageHeader (for displaying the reaction
count), but the autoscroll trigger is independent of the header - it fires from
the MessageList autoscroll effect re-running on reaction updates.
// Channel + MessageList setup (trimmed to relevant props)
export const reactionData: ReactionData[] = [{ Icon: LoveReaction, type: 'love' }];
<Channel
channel={channel}
MessageHeader={MessageHeaderComponent}
supportedReactions={reactionData}
messageActions={messageActions} // copy / quotedReply / edit / flag only
deletedMessagesVisibilityType="never"
maxTimeBetweenGroupedMessages={120000}
getMessageGroupStyle={getMessageGroupStyle}
forceAlignMessages="left"
>
<MessageList />
</Channel>
Offline support
Environment
Click To Expand
package.json:
{
"name": "ehfixsdk54",
"main": "expo-router/entry",
"version": "1.0.0",
"dependencies": {
"@aws-amplify/react-native": "^1.1.11-unstable.96fdd13.0",
"@clerk/clerk-expo": "^2.15.2",
"@expo/metro-runtime": "~6.1.2",
"@expo/vector-icons": "^15.0.2",
"aws-amplify": "^6.15.6",
"expo": "~54.0.10",
"expo-router": "~6.0.8",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-native": "0.81.4",
"react-native-gesture-handler": "~2.28.0",
"react-native-reanimated": "~4.1.1",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0",
"react-native-svg": "15.12.1",
"react-native-web": "~0.21.0",
"react-native-worklets": "0.5.1",
"stream-chat-expo": "^8.12.4",
"zustand": "^5.0.8"
}
}
Resolved Stream versions:
stream-chat-expo: 8.12.4
stream-chat-react-native-core: 8.12.4
stream-chat: 9.32.0
New Architecture (Fabric) is enabled (newArchEnabled: true in app.json).
- Platform that you're experiencing the issue on:
stream-chat-react-native version you're using that has this issue:
8.12.4 (stream-chat-expo 8.12.4)
- Device/Emulator info:
- [x ] I am using a physical device
- OS version: 26.5
- Device/Emulator: iPhone 12 mini
Additional context
Root-cause analysis
Adding a reaction mutates channel state, which produces a new messages array
reference. In useMessageList, processedMessageList is a useMemo keyed on
messageList, so it recomputes on any reaction/edit (not just on new messages):
// package/src/components/MessageList/hooks/useMessageList.ts
const processedMessageList = useMemo<LocalMessage[]>(() => {
// ...
}, [messageList, deletedMessagesVisibilityType, client.userID, isFlashList]);
That recompute re-runs the autoscroll effect in MessageList.tsx. When the list
is already on the latest set, latestNonCurrentMessageBeforeUpdateRef.current is
undefined, so didMergeMessageSetsWithNoUpdates evaluates to false, which
forces setAutoscrollToRecent(true) and recreates maintainVisibleContentPosition
— even though this was a content-only update (a reaction), not a new message:
// package/src/components/MessageList/MessageList.tsx
const didMergeMessageSetsWithNoUpdates =
latestNonCurrentMessageBeforeUpdate?.id === latestCurrentMessageAfterUpdate.id;
// undefined === <id> -> false
const shouldForceScrollToRecent =
!didMergeMessageSetsWithNoUpdates ||
processedMessageList.length - messageListLengthBeforeUpdate.current > 0;
// -> true
if ((maximumMessageLimit && shouldForceScrollToRecent) || !maximumMessageLimit) {
setAutoscrollToRecent(shouldForceScrollToRecent); // -> true on a reaction
}
Because the reaction also changes the rendered height of the message, the
maintainVisibleContentPosition re-anchor (minIndexForVisible: 1) on the New
Architecture FlatList shifts the viewport, producing the upward jump. The effect
cannot currently distinguish a genuine new/removed message from a content-only
update (reaction/edit).
This logic is unchanged on the current develop branch
(MessageList.tsx on develop),
so the latest releases are affected too.
Suggested fix
In the autoscroll effect, skip setAutoscrollToRecent(true) when the update is
content-only — i.e. when the message count is unchanged
(processedMessageList.length === messageListLengthBeforeUpdate.current) and
the latest message id is unchanged. That prevents reactions/edits from triggering
an autoscroll, matching the behavior restored on Android (#5280).
Issue
When viewing a channel at (or near) the bottom of the message list, adding a
reaction (e.g. a "like"/love) to a recent message causes the
MessageListtounexpectedly scroll upward, away from the message the user just reacted to. A
common trigger: a new message arrives, the user taps to "like" it, and the list
immediately jumps up (it appears to land around older/unread messages).
The reacting interaction itself uses the SDK's default reaction handler - there
is no custom reaction send/toggle logic. The list is a standard
<MessageList />inside
<Channel>.This appears to be the React Native counterpart of two bugs already reported and
fixed in the sibling SDKs:
Steps to reproduce
Steps to reproduce the behavior:
message.
Expected behavior
Adding a reaction should not move the scroll position - the list should stay
anchored on the message the user reacted to (the behavior restored by the Android
fix #5280).
Project Related Information
Customization
Click To Expand
The bug reproduces with a standard
MessageList. OurChannelconfigures asingle custom reaction and a custom
MessageHeader(for displaying the reactioncount), but the autoscroll trigger is independent of the header - it fires from
the
MessageListautoscroll effect re-running on reaction updates.Offline support
Environment
Click To Expand
package.json:{ "name": "ehfixsdk54", "main": "expo-router/entry", "version": "1.0.0", "dependencies": { "@aws-amplify/react-native": "^1.1.11-unstable.96fdd13.0", "@clerk/clerk-expo": "^2.15.2", "@expo/metro-runtime": "~6.1.2", "@expo/vector-icons": "^15.0.2", "aws-amplify": "^6.15.6", "expo": "~54.0.10", "expo-router": "~6.0.8", "react": "19.1.0", "react-dom": "19.1.0", "react-native": "0.81.4", "react-native-gesture-handler": "~2.28.0", "react-native-reanimated": "~4.1.1", "react-native-safe-area-context": "~5.6.0", "react-native-screens": "~4.16.0", "react-native-svg": "15.12.1", "react-native-web": "~0.21.0", "react-native-worklets": "0.5.1", "stream-chat-expo": "^8.12.4", "zustand": "^5.0.8" } }Resolved Stream versions:
stream-chat-expo: 8.12.4stream-chat-react-native-core: 8.12.4stream-chat: 9.32.0New Architecture (Fabric) is enabled (
newArchEnabled: trueinapp.json).stream-chat-react-nativeversion you're using that has this issue:8.12.4(stream-chat-expo 8.12.4)Additional context
Root-cause analysis
Adding a reaction mutates channel state, which produces a new
messagesarrayreference. In
useMessageList,processedMessageListis auseMemokeyed onmessageList, so it recomputes on any reaction/edit (not just on new messages):That recompute re-runs the autoscroll effect in
MessageList.tsx. When the listis already on the latest set,
latestNonCurrentMessageBeforeUpdateRef.currentisundefined, sodidMergeMessageSetsWithNoUpdatesevaluates tofalse, whichforces
setAutoscrollToRecent(true)and recreatesmaintainVisibleContentPosition— even though this was a content-only update (a reaction), not a new message:
Because the reaction also changes the rendered height of the message, the
maintainVisibleContentPositionre-anchor (minIndexForVisible: 1) on the NewArchitecture FlatList shifts the viewport, producing the upward jump. The effect
cannot currently distinguish a genuine new/removed message from a content-only
update (reaction/edit).
This logic is unchanged on the current
developbranch(MessageList.tsx on develop),
so the latest releases are affected too.
Suggested fix
In the autoscroll effect, skip
setAutoscrollToRecent(true)when the update iscontent-only — i.e. when the message count is unchanged
(
processedMessageList.length === messageListLengthBeforeUpdate.current) andthe latest message id is unchanged. That prevents reactions/edits from triggering
an autoscroll, matching the behavior restored on Android (#5280).