Skip to content

FlashList v2 Chat UX Regression: Inconsistent scroll behavior with paginated messages #1844

@EL-BADI

Description

@EL-BADI

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:

  1. Initial load: Fetch most recent N messages
  2. User scrolls to view older messages
  3. Trigger pagination to load next batch of older messages
  4. 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:

  1. Visual Layout: Messages appear with newest at top (like Twitter/social feeds)
  2. Loading Pattern: Must scroll to opposite direction to load more content
  3. 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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions