-
Notifications
You must be signed in to change notification settings - Fork 336
Description
Summary
After migrating from FlashList v1 to v2, chat interfaces with paginated message loading experience a confusing UX regression where the scroll direction for viewing messages and loading more messages are inconsistent.
Expected Behavior (v1 with inverted={true}
)
- ✅ Latest messages appear at the bottom
- ✅ Users scroll up to see older messages
- ✅ Users scroll up to trigger
onEndReached
and load more messages - ✅ Consistent scroll direction for both viewing and loading
Current Behavior (v2 with maintainVisibleContentPosition
)
- ❌ Latest messages appear at the top
- ❌ Users scroll down to see older messages
- ❌ Users must scroll up to trigger
onStartReached
and load more messages - ❌ Inconsistent scroll direction: down to view, up to load more
Context
This issue occurs in a chat application where messages are loaded via paginated Convex queries. The typical flow is:
- Initial load: Fetch most recent N messages
- User scrolls to view older messages
- Trigger pagination to load next batch of older messages
- New messages arrive in real-time and are appended
Problem Description
In v2, the deprecation of inverted
prop forces developers to use maintainVisibleContentPosition
with startRenderingFromBottom: true
. However, this creates a fundamental UX issue:
- Visual Layout: Messages appear with newest at top (like Twitter/social feeds)
- Loading Pattern: Must scroll to opposite direction to load more content
- User Confusion: Users expect to scroll in one direction for both viewing and loading
Code Examples
v1 Implementation (Working UX with Convex pagination)
<FlashList
data={messages} // From usePaginatedQuery(api.messages.list)
renderItem={renderMessage}
inverted={true}
onEndReached={() => {
// Load more older messages via Convex pagination
loadMore();
}}
onEndReachedThreshold={0.5}
/>
v2 Implementation (Broken UX with Convex pagination)
<FlashList
data={messages} // From usePaginatedQuery(api.messages.list)
renderItem={renderMessage}
maintainVisibleContentPosition={{
startRenderingFromBottom: true,
autoscrollToBottomThreshold: 0.2,
}}
onStartReached={() => {
// Load more older messages - but user scrolled DOWN to see old messages!
loadMore(); // This creates cognitive dissonance
}}
onStartReachedThreshold={0.5}
/>
Workaround Attempts
1. Using onEndReached
instead of onStartReached
<FlashList
data={messages}
maintainVisibleContentPosition={{
startRenderingFromBottom: true,
}}
onEndReached={loadMoreMessages} // This doesn't work as expected
onEndReachedThreshold={0.5}
/>
Result: onEndReached
doesn't trigger when scrolling up from the bottom.
2. Reversing data and using regular top-to-bottom layout
const reversedMessages = [...messages].reverse();
<FlashList
data={reversedMessages}
maintainVisibleContentPosition={{
autoscrollToTopThreshold: 0.2,
}}
onEndReached={loadMoreMessages}
onEndReachedThreshold={0.5}
/>
Result: Loses the natural chat feel where new messages appear at the bottom.
Proposed Solutions
Option 1: Bring back inverted
prop
The simplest solution would be to un-deprecate the inverted
prop and maintain it alongside maintainVisibleContentPosition
.
Option 2: Smart callback detection
FlashList could automatically detect when maintainVisibleContentPosition.startRenderingFromBottom
is true and swap the behavior of onStartReached
and onEndReached
to maintain consistent UX.
Option 3: New chat-specific props
Add dedicated chat interface props:
<FlashList
chatMode={true} // Enables chat-optimized behavior
onLoadMoreMessages={loadMoreMessages}
/>
Impact
This affects every chat application using paginated queries (like Convex, Firebase, or custom pagination APIs) when migrating from v1 to v2. The current v2 implementation forces developers to choose between:
- Maintaining familiar chat UX (latest messages at bottom) with confusing load-more behavior
- Having consistent scroll directions but unfamiliar message layout
The pagination aspect makes this particularly problematic because users expect a natural flow: scroll to see older content → load more older content in the same scroll direction.
Environment
- FlashList: ^2.0.3
- React Native: [your RN version]
- Platform: Android
Additional Context
Chat interfaces are one of the most common use cases for FlashList, and this regression significantly impacts user experience. The v1 inverted
approach was intuitive and worked perfectly for chat applications.
Is there a recommended pattern for maintaining v1-style chat UX in v2, or would the team consider addressing this as a feature gap?