Skip to content

ReanimatedSwipeable | slow performance scrolling #3307

Closed

Description

@erie-e9

Description

FPS down when I'm using ReanimatedSwipeable from import Swipeable from 'react-native-gesture-handler/ReanimatedSwipeable' in each item Items are not been loaded until fetch from API is done, so this is not a load data issue. If I replace it for Swipeable from import Swipeable from 'react-native-gesture-handler/Swipeable' all work great

This issue is happening in Dev and Release apps and using Hermes + RN's New arch.

Note: I applied FlatList performance strategies and tested it with/without them and the issue is still happening.

Steps to reproduce

  1. Run app on real device (issue is most visible on Android devices).
  2. Turn on Perf Monitor.
  3. Create a List with items using FlatList
  4. Add Swipeable actions for each item.
  5. Scroll down/up list.

Snack or a link to a repository

https://github.com

Gesture Handler version

2.21.0, 2.21.1 and 2.21.2

React Native version

0.76.5

Platforms

Android, iOS

JavaScript runtime

Hermes

Workflow

React Native (without Expo)

Architecture

Fabric (New Architecture)

Build type

Release mode

Device

Real device

Device model

Huawei P30 Pro, iPhone 16 Pro Max

Acknowledgements

Yes

Activity

  1. github-actions commented on Dec 21, 2024

    @github-actions

    Hey! 👋

    The issue doesn't seem to contain a minimal reproduction.

    Could you provide a snack or a link to a GitHub repository under your username that reproduces the problem?

  2. latekvo commented on Jan 9, 2025

    @latekvo
    Member

    Hi, I tested both the legacy, and the reanimated Swipeable on the main branch, using the FlashList and useBenchmark from @shopify/flash-list.
    I didn't notice any performance advantage in using the legacy Swipeable over the reanimated Swipeable, both averaged 30fps on Samsung S21 Ultra.

    Test code - press to uncollapse
    import React, { Text, StyleSheet, View, Alert } from 'react-native';
    
    import ReanimatedSwipeable from 'react-native-gesture-handler/ReanimatedSwipeable';
    import { FlashList, ListRenderItem, useBenchmark } from '@shopify/flash-list';
    import { memo, useRef } from 'react';
    import {
      GestureHandlerRootView,
      // Swipeable as LegacySwipeable,
    } from 'react-native-gesture-handler';
    
    function RightAction() {
      return <Text style={styles.rightAction}>Text</Text>;
    }
    
    type Item = {
      id: string;
      testID: string;
      title: string;
    };
    
    const generateItems = (count: number): Item[] => {
      return Array(count)
        .fill(0)
        .map((value, idx) => {
          // render speed test
          return {
            id: String(idx),
            testID: String(idx),
            title: `Number ${idx}`,
          };
    
          // re-renders test
          // return {
          //   id: String(idx),
          //   testID: String(value),
          //   title: `Number ${value}`,
          // };
        });
    };
    
    const data = generateItems(200);
    
    const _RenderItemView = (item: Item) => (
      <View style={styles.swipeable}>
        <Text>{item.title}</Text>
      </View>
    );
    
    const RenderItemViewMemoed = memo(_RenderItemView);
    
    export default function Example() {
      const renderItem: ListRenderItem<Item> = ({ item }) => {
        return (
          <ReanimatedSwipeable
            renderRightActions={RightAction}
            testID={item.testID}>
            <RenderItemViewMemoed {...item} />
          </ReanimatedSwipeable>
        );
      };
    
      const flashListRef = useRef<FlashList<Item> | null>(null);
      useBenchmark(flashListRef, (callback) => {
        Alert.alert('result', callback.formattedString);
      });
    
      return (
        <GestureHandlerRootView>
          <FlashList
            ref={flashListRef}
            data={data}
            renderItem={renderItem}
            estimatedItemSize={50}
            keyExtractor={(item) => item.id}
          />
        </GestureHandlerRootView>
      );
    }
    
    const styles = StyleSheet.create({
      leftAction: { width: 50, height: 50, backgroundColor: 'crimson' },
      rightAction: { width: 50, height: 50, backgroundColor: 'purple' },
      separator: {
        width: '100%',
        borderTopWidth: 1,
      },
      swipeable: {
        height: 50,
        backgroundColor: 'papayawhip',
        alignItems: 'center',
      },
    });

    Does this issue still occur for you on version 2.22.0-rc.0?
    If so, could you please provide a reproduction where the difference is visible?

  3. added
    Close when staleThe issue will be closed automatically if it remains inactive
    on Jan 9, 2025
  4. davelynInes commented on Jan 15, 2025

    @davelynInes

    I also encounter the slow performance on scrolling on ReanimatedSwipeable, it looks jittery when scrolled, but only when I use the methods like onSwipeableOpen or onSwipeableClose. When I remove these, it's back to normal.

    <ReanimatedSwipeable
    //containerStyle={[style]}
    ref={reanimatedRef}
    friction={2}
    leftThreshold={80}
    rightThreshold={80}
    renderLeftActions={LeftAction}
    renderRightActions={RightAction}
    onSwipeableOpen={() => {
    console.log('jittery');
    }}
    >
    {children}

    Can you please try and check this?

  5. added
    Can reproIt is confirmed we can reproduce the issue
    and removed on Jan 16, 2025
  6. Baart commented on Feb 12, 2025

    @Baart

    I second the usage of onSwipeableOpen={() => { }} leads to huge stutter.

    @davelynInes have you found any workaround for using onSwipeableOpen without having an unusable app ?

  7. davelynInes commented on Feb 12, 2025

    @davelynInes

    I second the usage of onSwipeableOpen={() => { }} leads to huge stutter.

    @davelynInes have you found any workaround for using onSwipeableOpen without having an unusable app ?

    I used the old/legacy react-native-gesture-handler/Swipeable for now. I didn't have issues with it.

  8. RohovDmytro commented on Feb 19, 2025

    @RohovDmytro

    I confirm the new Swipeable is very slow within an Animated.FlatList.

  9. zrina1314 commented on Mar 3, 2025

    @zrina1314

    I second the usage of onSwipeableOpen={() => { }} leads to huge stutter.
    @davelynInes have you found any workaround for using onSwipeableOpen without having an unusable app ?

    I used the old/legacy react-native-gesture-handler/Swipeable for now. I didn't have issues with it.

    What version are you using

  10. roo12312 commented on May 4, 2025

    @roo12312

    I second the usage of onSwipeableOpen={() => { }} leads to huge stutter.

    @davelynInes have you found any workaround for using onSwipeableOpen without having an unusable app ?

    I used the old/legacy react-native-gesture-handler/Swipeable for now. I didn't have issues with it.

    Same here... Old swipeable is so clean and fast in android

  11. mysticmew7 commented on Sep 3, 2025

    @mysticmew7

    I added ReanimatedSwipeable to a SectionList and JS FPS dropped from 60 to much lower during scrolling and other animations.

    I spent a few hours trying to figure out if there was something wrong with my code and optimize performance.
    Commenting out the wrapping brought back buttery performance.
    It must be either creating a bunch of views, or running a bunch of code, because it's a serious drain on performance.

  12. RohovDmytro commented on Sep 20, 2025

    @RohovDmytro

    Can confirm that The drain in performance is is huge, almost doubling rendering time of each of my list item.

  13. pablogdcr commented on Oct 29, 2025

    @pablogdcr

    Hi, I'm still facing the same issue: Expo 54, React-Native 0.81.5, new architecture.
    FPS drops a lot when scrolling.

  14. demetrio commented on Nov 2, 2025

    @demetrio

    Just to try to bump this issue: I'm having the same problem.
    I tried what @davelynInes suggested about using the old/legacy react-native-gesture-handler/Swipeable and it worked.

    I noticed that there are other PRs and/or issues related to this too, so it would be great if this issue could be priorised 🙏🏻
    Have a good day.

  15. hirbod commented on Nov 8, 2025

    @hirbod

    So @davelynInes post (at the top of the thread) sparked my attention.

    What we know is that setting up Gesture is a very expensive operation, and it’s usually recommended to useMemo the gesture. ReanimatedSwipeable tries that, but after inspecting the code I can see that onSwipeableOpen and onSwipeableClose are directly passed as dependencies to dispatchEndEvents and dispatchImmediateEvents, which are dependencies for animateRow. Long story short, that simple prop causes the whole memoization chain to break, rendering every effort to memoize the Gesture useless. That’s the ugly and tricky part about useCallback and useMemo: a single mistake is enough.

    You could try to pass a stable function to onSwipeableOpen with useCallback and see if that fixes the issue. If that doesn’t work, ReanimatedSwipeable itself should be wrapped with memo on top of that. But ReanimatedSwipeable needs fully stable props to prevent tapGesture from being re-created over and over again.

    I did not verify my findings, but I am pretty sure that this could be the culprit.

    One more hint: if used with FlatList, which uses no recycling, using ReanimatedSwipeable is not recommended, as it will re-create the item every time, and the gesture setup is definitely expensive.
    Use Flashlist, or better, LegendList

  16. chocky335 commented on Feb 21, 2026

    @chocky335
    Contributor

    We had the same performance issue with reanimated swipeable, so we created our own native swipeable with great performance. It's open source. You can find it here -> https://www.npmjs.com/package/react-native-swipeable-actions

  17. ngocdevv commented on Aug 22, 2026

    @ngocdevv
    Contributor

    Opened a fix for the inline-callback memoization break: #4466

    onSwipeableOpen / onSwipeableClose (and the other event props) were in the worklet dependency chain, so a new function identity on each parent render reconfigured the native pan/tap handlers. The PR keeps those callbacks behind stable wrappers and adds a regression test.

  18. added a commit that references this issue on Aug 24, 2026
    5de6d23
  19. added a commit that references this issue on Sep 11, 2026
    706064d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Can reproIt is confirmed we can reproduce the issuePlatform: AndroidThis issue is specific to AndroidPlatform: iOSThis issue is specific to iOS

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions