react-native-reanimated icon indicating copy to clipboard operation
react-native-reanimated copied to clipboard

Performance Degradation on Android after New Architecture Migration

Open mimo-10 opened this issue 8 months ago • 70 comments

Description

After upgrading React Native to the New Architecture, react-native-reanimated causes severe performance issues on Android. Animations that previously ran smoothly now stutter and lag heavily, making the app nearly unusable.

I tested Reanimated versions from 3.16.0 to 4.0.0, and none of them resolved the issue. Performance is excellent on the Old Architecture, but significantly degraded with the New Architecture — only on Android.

Steps to reproduce

Steps to reproduce the behavior:

  1. Setup a React Native project with New Architecture enabled.
  2. Add a simple screen with Reanimated-based animations (nerly all type of animation will cause that).
  3. Add a Pressable or Touchable element that triggers animation. (prefered if this elements are also animated)
  4. Run the app on Android, you will notice severe animation lag or stuttering.
  5. Run the same app on Old Architecture — animations are smooth.
  6. Run the same app on iOS — animations are smooth.

Screenshots or video

Can provide video comparison if needed (Old Arch vs. New Arch on Android).

Many libraries we rely on require the New Architecture, and all work great except Reanimated.

This issue is blocking our production work. We’ve confirmed it’s not our app logic — the problem occurs on minimal examples too. It’s easily reproducible.

This bug is affecting multiple teams and developers. There are many reports and mentions of degraded Reanimated performance on Android after enabling the New Architecture.

  • Is this a known issue?
  • Are there any workarounds or optimizations we can try?
  • Is a fix in progress?

🙏 Thank you!

We appreciate the amazing work the Reanimated team does and hope this can be resolved soon. Please let us know how we can help in debugging or testing.

Snack or a link to a repository

https://snack.expo.dev/@abdelalim_chicha/19f04f

Reanimated version

3.17.4

React Native version

0.78

Platforms

Android

JavaScript runtime

Hermes

Workflow

Expo Dev Client

Architecture

Fabric (New Architecture)

Build type

Debug app & dev bundle

Device

Real device

Host machine

Linux

Device model

Galaxy s9 with android 10

Acknowledgements

Yes

mimo-10 avatar Apr 25 '25 22:04 mimo-10

i did upload all the code but from the snack file u could understand type aniamtions there, just simple once on a sign up screen makes the app unsabe, again thank for your help : )

mimo-10 avatar Apr 25 '25 22:04 mimo-10

Hello any updates,

mimo-10 avatar May 03 '25 14:05 mimo-10

i did upload all the code but from the snack file u could understand type aniamtions there, just simple once on a sign up screen makes the app unsabe, again thank for your help : )

I have run the repro you have provided but haven't noticed any performance regressions.

Can you tell us more specifically what is the problem? I'm afraid "performance degradation" is too broad for us to investigate. We need to know exactly how to reproduce the problem if you want us to fix it.

tomekzaw avatar May 05 '25 07:05 tomekzaw

Thanks for getting back to me.

I understand the need for a detailed reproduction, and I will create one as soon as I have the time. I plan to provide two versions—one using the old architecture and one using the new architecture—to clearly show the differences. Unfortunately, I'm currently very busy and can’t put that together right away.

That said, I want to emphasize that this issue seems to be directly related to the upgrade. While there isn’t a single specific case to isolate it, the problem becomes very noticeable when there are more than 3–4 animations running simultaneously. In such scenarios, the performance drops significantly, but only on Android—iOS remains smooth.

As a general suggestion, if you take any existing app that includes animations and build it with the new architecture enabled, you should be able to spot the degradation compared to the same app running on the old architecture.

I’ve also seen several other developers mention the same issue on Twitter, so it doesn’t seem to be isolated to my project.

Thanks again, and I’ll follow up with the detailed reproduction as soon as possible.

mimo-10 avatar May 17 '25 18:05 mimo-10

Also i have check #7464 and i seems related, bu the problem is not related to sdk 53, it is from the first time reanimated adapted the new arch.

mimo-10 avatar May 17 '25 18:05 mimo-10

Please check #6559 , you could see in the videos what I mean.

mimo-10 avatar Jun 03 '25 14:06 mimo-10

I mean this one too #6710

mimo-10 avatar Jun 03 '25 14:06 mimo-10

Also related #7460

genesiscz avatar Jun 04 '25 10:06 genesiscz

Hello any updates !? , at least tell us if u guys are working on this, this very very critical; (

mimo-10 avatar Jul 01 '25 18:07 mimo-10

any updates I am having significant issues on IOS!!

jdicami avatar Jul 17 '25 20:07 jdicami

I have bought and iPhone and a MacBook in order to finish my work, for now iOS version works great, 3.18.0 , v4 beta 3 is totally broken , so if your are using it just downgrade and everything should work fine enough

mimo-10 avatar Jul 17 '25 20:07 mimo-10

I've used 3.17 and 4.0.1 with bare react native project, just plain translateX animation not runs at 120 FPS on Pro Motion screen on IOS. It's a disaster, I'm not expecting 120 FPS on heavy animated or complex screens, but for plain simple animations at my point of view it must be 120 FPS

Navipro70 avatar Aug 02 '25 17:08 Navipro70

@Navipro70 Can you send us a repro? Do you use useAnimatedScrollHandler? Are there React commits happening during the animation? How exactly do you measure UI FPS so you know it's not 120 fps?

tomekzaw avatar Aug 02 '25 19:08 tomekzaw

@tomekzaw Hello, thank you very much for a fast reply!

I was working hard to make a reproducible repo, now I have it at my personal application. I've created a sample native module with Fabric View which just present a simple animation thought UIView to compare similar behavior with reanimated and animated (e.g. modal like animation), so I got the next benchmark results (by my eyes only, unfortunately):

  1. Native View 120 FPS
  2. Animated ~60 FPS
  3. Reanimated ~30-60 FPS

Those values are being provided from my point of view at release scheme builded on a physical device, without tool to measure, because I don't know how to measure it and what tool should I use, I don't know how to measure 120 FPS in react-native IOS device (perf monitor is 60 FPS, flashlight is android only), but moreover it seems that fps is dropping for exact animation and component, because I've found this reanimated lagging behavior on my animated header with scroll and when I was releasing the finger to start header animation I saw both scroll and header translations, and scroll was smooth like 120 FPS, when the header transition was worser (30-60 FPS).

I also tested release scheme build on a simulator and didn't saw any performance issues, all examples were working at 120 FPS (also my eyes benchmark 😅). To my defence, I'd want to say that every friend of mine who tested my application said that animations aren't smooth enough.

Answering your questions:

  1. Tried useAnimatedScrollHandler, onScroll by itself and onScroll like a 'worklet' - met the same behavior
  2. Checked Profiler for a scroll example, there were just <0.1ms renderers without components rerenders
  3. Already said a lot about measuring, unfortunately it's my and my friends point of view cuz I don't know a tool for measuring and I'm assuming that it will not help us, because problem is related only to reanimated part and don't impact the Phone FPS

Tomorrow will drop a repo with source code, need some time to bring the files and asure the consistency of reproduction

Navipro70 avatar Aug 03 '25 02:08 Navipro70

I've found this reanimated lagging behavior on my animated header with scroll and when I was releasing the finger to start header animation I saw both scroll and header translations, and scroll was smooth like 120 FPS, when the header transition was worser (30-60 FPS).

Does it look somewhat similar to this video recording (i.e. animated header is out sync with scroll offset)?

https://github.com/user-attachments/assets/896ff146-9297-492e-90f6-06c5dcb68ae0

tomekzaw avatar Aug 03 '25 08:08 tomekzaw

@tomekzaw no, it doesn't related to a sticky header, scroll position and etc. I show and hide my header after 200 px scrolling from top of a screen, by signle call withTiming to translateX header and show to user

Navipro70 avatar Aug 03 '25 12:08 Navipro70

@tomekzaw Also experiencing performance degradation over here that seems to be related to ScrollViews and the amount of Reanimated components that are rendered at a given point in time.

To preface, I am not using useAnimatedScrollHandler and there are no React commits happening when the lag occurs. In my case, there is heavy stuttering when panning any ScrollView while there is a significant amount of Reanimated components rendered outside of the ScrollView itself (completely different component, not a sibling of the ScrollView). Other parts of the app, including animations, are not affected regardless of how many Reanimated components are mounted on the DOM.

Once I reduce the amount of Reanimated components being rendered, the ScrollView stuttering does not occur anymore. This only happens on the New Architecture, tested on Reanimated v4.

unendingblue avatar Aug 03 '25 13:08 unendingblue

The original issue is related only for Android, so I've created another one with highly described reproducible steps and repository example.

@mimo-10 Did you tested performance on a Pro Motion screen in released application? I think you should run into the same behavior as me for Pro Motion devices

Navipro70 avatar Aug 03 '25 15:08 Navipro70

No, I haven’t tested it on a ProMotion screen. I tested it on an iPhone 13. The production app performs reasonably well — although it doesn’t consistently reach 60 FPS on many screens, it’s acceptable for now. On Android, the performance is slightly worse. For example, on a Galaxy S9, the production version struggles more and fails to reach 60 FPS on more screens.

I also tested it on a OnePlus 11R. It can reach 120 FPS when no animations are running, but as soon as any animation is triggered, performance drops significantly — sometimes barely hitting 30 FPS. The issue doesn’t seem to be related to scroll handlers specifically (though adding them does make performance even worse). Instead, the problem appears to be with running multiple animations simultaneously. When only one or two animations are active, everything runs smoothly. But with more than that, performance becomes very unstable — especially during development. Again, I recommend reviewing the changes introduced between version 3.15 and 3.16. The shift to the new architecture seems to be the cause, since the older architecture worked perfectly fine. I would like also to highlight that sometimes fps gets as low as 1 to 3 fps.

mimo-10 avatar Aug 03 '25 15:08 mimo-10

No, I haven’t tested it on a ProMotion screen. I tested it on an iPhone 13. The production app performs reasonably well — although it doesn’t consistently reach 60 FPS on many screens, it’s acceptable for now

I do think we have similar problems, I don't run android cuz no need it for now. On an iPhone with Pro Motion you should meet the same behavior as me (60 FPS or even lower). I've changed FPS to 60 on my iPhone and tested the UI - all looks normal (laggy for me after 120 😅), but native animations and reanimated animations are exactly the same, however reanimated sometimes was dropping lower then 60 FPS for very simple behavior like a "open modal by pressing the button" on a Pro Motion if you run animation like 5-20 times, it starts performing 30 FPS (from my view)

@tomekzaw maybe this info also will help

Navipro70 avatar Aug 03 '25 15:08 Navipro70

I’ll be sharing some videos soon to show how the app performs — particularly on the iPhone 13. iOS handles things quite well, but on Android, the performance is significantly worse to the point of being almost unusable.

To demonstrate that the issue is specifically caused by Reanimated (and not React itself, the new architecture, or any related library), I created two nearly identical components. Both are simple and use just a couple of SharedValues — one uses the standard Animated API, and the other uses Reanimated.

Both components were tested in the same environment. The version using the Animated API has virtually no impact on FPS, which makes sense given the simplicity of the animation and the capability of the device.

However, the Reanimated version clearly causes a noticeable frame drop — sometimes as much as 15 FPS, down to around 45 FPS. Interestingly, the drop isn’t immediate; it still even after the animation finished. This means that if any additional animations are triggered afterward, performance degrades even further.

https://github.com/user-attachments/assets/44735ed2-e0a2-43db-b0d1-38b0081c447d

**side-note : usenativedriver is set to true , also "add poll" animated api , "add quiz" reanimated. **

Moving on, this next video demonstrates how useScrollHandler behaves unstably — even on iOS. The performance degradation occurs only when animations are running. At times, the frame rate drops as low as 4 FPS. While some slowdown is acceptable, having animations completely freeze like this should never happen.

It’s worth mentioning that this scroll behavior works fine in the iOS production build. However, horizontal scroll performance is noticeably worse — even in the production version — and doesn’t perform as well as expected.

I also tested the exact same screens using animations created with the Animated API, and the performance was excellent — even in the development build. Additionally, when there are no animations, horizontal scrolling runs at a stable 60 FPS.

https://github.com/user-attachments/assets/121851a3-046a-4d90-9d8a-65b5ea69cf2a

This strongly suggests that the degradation happens only when the horizontal scroll indicator (the purple one) is moving, which confirms that Reanimated is likely the bottleneck.

https://github.com/user-attachments/assets/8edfd703-e249-4956-be9d-d85cf292abe9

Last but not least, I created this example to demonstrate that the scroll handler itself isn’t the root issue. When there are no animated components on the screen (aside from the bottom bar), the FPS remains stable at 60 FPS on iOS.

On Android, the performance is slightly lower — around 53–55 FPS — but still within an acceptable range.

This further confirms that the performance problems arise not from useScrollHandler alone, but rather when it’s combined with Reanimated components or when multiple animations are active at the same time.

Side-note: All of these animations perform significantly better on the old architecture. In fact, in the last video, you can clearly see the animation glitching — it’s not smooth at all. Previously, I had bottom sheets with even more components (including Reanimated ones), and they ran flawlessly — even on Android — when using the old architecture. That level of performance is no longer achievable with the new setup, despite the content being simpler.

https://github.com/user-attachments/assets/bf01fdca-dd7a-437c-8a7c-67d0289faa5f

This is in the same app with a page that does not have more than 2 animated component( it barely degrades)

https://github.com/user-attachments/assets/2373b483-d165-42ab-a96d-359494e1ce2d

mimo-10 avatar Aug 03 '25 16:08 mimo-10

Sorry forget to embed this for scroll behavior

https://github.com/user-attachments/assets/12e220e8-3ccb-4767-8d41-47bf581496c0

mimo-10 avatar Aug 03 '25 19:08 mimo-10

What a mess!

I've found that I didn't set flag at my Info.plist CADisableMinimumFrameDurationOnPhone to true (I didn't have it at all)

After adding this flag all animations become smooth! So my personal IOS issue wasn't related to a performance degradation, I think I've missed the documentation or CADisableMinimumFrameDurationOnPhone was missed at all.

Navipro70 avatar Aug 03 '25 21:08 Navipro70

@tomekzaw news ?!

mimo-10 avatar Aug 06 '25 00:08 mimo-10

@mimo-10 What kind of news do you expect? I've already tested out the repro you have provided in the issue description and I couldn't reproduce the problem (https://github.com/software-mansion/react-native-reanimated/issues/7435#issuecomment-2850094218). We need to have a reproducible example that we can investigate and improve if you want us to fix it.

tomekzaw avatar Aug 06 '25 10:08 tomekzaw

Thanks @tomekzaw again for your response, As I mentioned earlier https://github.com/software-mansion/react-native-reanimated/issues/7435#issuecomment-2888526212 , the performance problem isn’t tied to a specific component or isolated case. It’s a more general issue that becomes apparent when multiple animated components (typically 3–4 or more) are present on the screen—particularly on Android (especaily, but now also with ios) with the new architecture enabled. Because of this, the issue might not reproduce with a minimal example unless the example includes enough complexity to trigger the degradation (e.g., several simultaneous animations). So the problem is more about the Reanimated’s ability to handle heavier animation loads (with new arch) , rather than any one particular edge case. Again as i mentioned (beacuse any example would perform better ( 5 to 10 times) with the old arch campared to the new one, i believe that it does not need specific reproduction, any app that you have built before for example could be a test case. That said, But if a reproducible example is the best way for the team to diagnose and resolve this, I’m happy to put one together. But could you please clarify exactly what kind of details you’d like me to include in the reproduction? anything that can help ? I just want to make sure I invest the time in creating something that’s genuinely useful for your investigation. I really believe this is a critical issue, especially for apps that rely heavily on animations, and I’d love to help however I can to get it addressed.

Thanks again!, have a nice day : )

mimo-10 avatar Aug 06 '25 12:08 mimo-10

@mimo-10 From our experience, Reanimated is able to handle way way more animated components than just four, even on very low-end Android devices in debug mode, on the New Architecture.

We have a fabric-example app in our repo and it works smoothly for us both on Android and iOS.

If we are to investigate this issue, we need a way to run the app on our setup and see the regressions on our own. If you are able to put it together, we'd be genuinely happy to help you. Otherwise, we can't really do anything on our end.

If you are not able to share the code of your app publicly (e.g. your project is a commercial app) but still would like us to take a look and investigate the performance issues, please drop us a line at [email protected] and we'll see how we can work together.

tomekzaw avatar Aug 06 '25 13:08 tomekzaw

@tomekzaw Could this be related to https://github.com/facebook/react-native/issues/51870?

unendingblue avatar Aug 06 '25 15:08 unendingblue

@unendingblue Partially yes, and also to https://github.com/facebook/react-native/issues/51869

tomekzaw avatar Aug 06 '25 15:08 tomekzaw

Hello,

i am experiencing also a significant performance issue after trying to upgrade to RN New Arch especially on android but also on ios. Everything works well with old arch, but not with the new one

I have build an Animated Flatlist with a complex Parallax Teaser (look in the attached video).

https://github.com/user-attachments/assets/eec86699-b240-4221-b81b-feee72c5cb54

The teaser includes 4 animations:

  1. translatation of image
  2. translation of text
  3. opacity of overlay mask
  4. stroke of masked svg image

The teaser retrieves two relevant arguments from the Flatlist:

scrollY: SharedValue<number>;
flatListLayoutInfo: SharedValue<{
    height: number;
    pageY: number;
} | undefined>; 

In the teaser we are calculating some values:

const imageContainerViewRef = useAnimatedRef<Animated.View>();
const imageContainerLayoutInfo = useSharedValue<
        {
            height: number;
            pageY: number;
        } | undefined
>(undefined);

 /*
 calculate depending on flatListLayoutInfo.value and imageContainerLayoutInfo.value
*/
 const animatedValues = useDerivedValue<AnimatedValues | undefined>(() => {
      ...
      return { intersectingValue, absIntersectingValue, halfImageHeight };
  }); 

 useDerivedValue(() => {
        if (imageContainerLayoutInfo.value) {
            return;
        }

        /*
            can be < 0 if pull to refresh was used
        */
        if (scrollY.value <= 0) {
            return;
        }

        const measured = measure(imageContainerViewRef);

        if (!measured) {
            return;
        }

        imageContainerLayoutInfo.value = {
            height: measured.height,
            pageY: measured.pageY + scrollY.value,
        };
    }, [scrollY.value]);
    
       /*
        animated style of dark overlay which
        fades in on scroll
    */
    const animatedOverlayStyle = useAnimatedStyle(() => {
        ...
        const { intersectingValue, absIntersectingValue, halfImageHeight } =
            animatedValues.value;
       ...
        return {
            position: 'absolute',
            top: 0,
            right: 0,
            bottom: 0,
            left: 0,
            backgroundColor: 'rgba(0,0,0,0.5)',
            opacity: interpolate(
                infosTranslateY,
                [0, halfImageHeight],
                [0, 0.5],
            ),
            pointerEvents: 'none',
        };
    });

    /*
        animated style of image translation
    */
    const animatedImageContainerStyle = useAnimatedStyle(() => {
       ...
        const { intersectingValue, absIntersectingValue, halfImageHeight } =
            animatedValues.value;
       ...
        return {
            transform: [
                {
                    translateY,
                },
            ],
        };
    });

    /*
        animated style of infos translation
    */
    const animatedInfosContainerStyle = useAnimatedStyle(() => {
        ...
        const { intersectingValue, absIntersectingValue, halfImageHeight } =
            animatedValues.value;
        ...
        return {
            transform: [
                {
                    translateY,
                },
            ],
        };
    })

In the animated Image, we calculate and animate the strokeWith based in the given parameter:

animatedValues: Readonly<SharedValue<{
    intersectingValue: number;
    absIntersectingValue: number;
    halfImageHeight: number;
} | undefined>>;

const animatedProps = useAnimatedProps(() => {
        ...
        const { intersectingValue, absIntersectingValue, halfImageHeight } =
            animatedValues.value;

        // image is cutted by header
        if (intersectingValue < 0) {
            // image scrolled until 50% -> translate strokeWidth to make image bigger
            if (absIntersectingValue <= halfImageHeight) {
                return {
                    strokeWidth: interpolate(
                        absIntersectingValue,
                        [0, halfImageHeight],
                        [strokeWidthDefault, MAX_STROKE_WIDTH],
                    ),
                };
            }
         ....
    });

The animatedProps are passed into a

<AnimatedPath
  ...
  animatedProps={animatedProps}
/>

created outside the component with

const AnimatedPath = Animated.createAnimatedComponent(Path);

THE RESULT:

On IOS, the animation is poor like in the video, but i can fix it with removing the animated Svg Path. On Android it is still poor, even if i remove the Svg Path.

Thanks for your time in advance! Greets Robert

zerobertelprivat avatar Aug 11 '25 06:08 zerobertelprivat