Skip to content

v5: add FlatList-like pull-to-refresh and onScroll (offset) for vertical feeds #952

Description

@pareshbbi

Is your feature request related to a problem? Please describe.

We migrated a vertical full-page reel feed (TikTok/Reels style) from FlashList / FlatList to react-native-reanimated-carousel v5.1.1.

Two FlatList-level APIs are missing, and the current v5 surface cannot replace them without fragile workarounds:

1. Pull-to-refresh (FlatList RefreshControl)

v5 has no refreshControl, onRefresh, or refreshing prop.

overscrollEnabled only rubber-bands and snaps back. It does not:

  • detect a pull past a threshold on the first item
  • call a refresh handler
  • keep a spinner visible while refreshing === true

onConfigurePanGesture is not a real replacement:

  • CarouselPanGesture is a facade that blocks runOnJS, enabled, and manualActivation
  • JS side effects must use scheduleOnRN
  • carousel-owned pan handlers run before consumer observers, so overscroll rebound fights the pull
  • there is no first-class “at index 0 + pulling backward” state
  • there is no built-in refresh indicator

Result: pull-to-refresh on a vertical carousel is custom, easy to break, and does not match FlatList UX.

2. onScroll (FlatList onScroll / NativeScrollEvent)

v5 lifecycle is only:

  • onScrollStart(): void — no offset, velocity, or direction
  • onSnapToItem(index: number) — fires after settle, not during the gesture

That is not enough for a feed that needs per-frame scroll:

  • prefetch the next page while the user is still dragging (not only after snap)
  • pause / duck video during the swipe
  • analytics (scroll depth, direction, velocity)
  • sync UI that is tied to pixel offset (progress bars, sticky chrome)

onProgressChange / progress / scrollOffsetValue are not a drop-in onScroll:

  • onProgressChange is JS every frame with a logical index, not NativeScrollEvent (contentOffset, velocity, contentSize, layoutMeasurement)
  • getCurrentIndex() is the last settled index, so it is wrong during an in-flight swipe
  • scrollOffsetValue is UI-thread / two-way and “does not commit selection or fire onSnapToItem
  • there is no onScrollBeginDrag / onScrollEndDrag / onMomentumScrollEnd equivalent with payload

So a FlashList feed that used onScroll + RefreshControl cannot be moved to v5 without reimplementing both.

Describe the solution you'd like

A. First-class pull-to-refresh

Support the FlatList mental model on a non-loop carousel, especially orientation="vertical":

<Carousel
  data={items}
  orientation="vertical"
  loop={false}
  refreshing={isRefreshing}
  onRefresh={() => refetch()}
  refreshControl={
    <RefreshControl
      refreshing={isRefreshing}
      onRefresh={refetch}
      tintColor="#fff"
    />
  }
  renderItem={renderItem}
/>

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions