How to Achieve 60FPS Animations in React Native

No items found.

In short

This article discusses the importance of achieving smooth 60FPS animations in React Native apps for a better user experience. It provides recommendations on optimizing animations, using native drivers, gesture-driven animations, and managing JavaScript operations to ensure a responsive and visually appealing mobile app.

Originally published in December 2021, updated in March 2024.

Nice and smooth animations are a crucial part of the success of your app. But, when done incorrectly, they can seriously damage your product by slowing it down. In this article, we describe some useful practices to achieve smooth 60FPS animations in React Native with no harm to the speed of your app.

The importance of smooth 60FPS animations on mobile devices

Mobile users are used to smooth and well-designed interfaces that quickly respond to their interactions and provide prompt visual feedback. As a result, applications have to register a lot of animations in many places that will have to run while other work is happening.

At the same time, the amount of information that can be passed over through the bridge is limited. There’s no built-in priority queue as of now. In other words, it is on you to structure and design your application so that both the business logic and animations can function without any disruptions. This is different from the way we are used to performing animations. For example, on iOS, the built-in APIs offer unprecedented performance and are always scheduled with the appropriate priority. Long story short – we don’t have to worry much about ensuring they’re running at 60 FPS.

With React Native, this story is a bit different. If you do not think about your animations top-down beforehand and choose the right tools to tackle this challenge, you’re on track to run into dropped frames sooner or later.

Janky animations make your app look slow and unfinished to users

In today’s sea of applications, providing a smooth and interactive UI might be one of your only ways to win over customers who are looking to choose the app to go.

If your application fails to provide a responsive interface that works well with the user interactions (such as gestures), not only may it affect new customers, but also decrease the ROI and user sentiment.

Mobile users like the interfaces that follow them along and that look top-notch, so ensuring the animations are always running smoothly is a fundamental part that builds such an experience.

One-off animations in React Native with native driver

Enabling the usage of native driver is the easiest way of quickly improving your animations' performance. However, the subset of style props that can be used together with the native driver is limited. You can use it with non-layout properties like transforms and opacity. It will not work with colors, height, and others. Those are enough to implement most of the animations in your app because you usually want to show/hide something or change its position.

For more complex use cases, you can use <rte-code>React Native Reanimated<rte-code> library. Its API is compatible with basic <rte-code>Animated<rte-code> library and introduces a set of fine-grained controls for your animations with a modern hooks-based interface. More importantly, it introduces the possibility to animate all possible style props with the native driver. So animating height or color will no longer be an issue. However, transform and opacity animations will still be slightly faster since they are GPU-accelerated. But regular users should not see any difference.

Ways to achieve gesture-driven 60FPS animations

The most desired effect that can be achieved with animations is being able to control animation with a gesture. For your customers, this is the most enjoyable part of the interface. It builds a strong sentiment and makes the app feel very smooth and responsive. Plain React Native is very limited when it comes to combining gestures with native driven animations. You can utilize <rte-code>ScrollView<rte-code> scroll events to build things like a smooth collapsible header.

For more sophisticated use cases, there is an awesome library, React Native Gesture Handler, which allows you to handle different gestures natively and interpolate those into animations. You can build a swipeable element by combining it with Animated. While it will still require JS callbacks, but there is a remedy for that!

The most powerful pair of tools for gesture-driven animations is using Gesture Handler combined with Reanimated. They were designed to work together and give the possibility to build complex gesture-driven animations that are fully calculated on the native side.

Reanimated API supports synchronous JavaScript execution on the UI thread using the concept of worklets. The library’s runtime spawns a secondary JS context on the UI thread that is then able to run JavaScript functions in the form of said worklets. Now using your imagination and leveraging Reanimated, you can create wonderful animations at full available speeds.

Low-level handling of gestures might not be a piece of cake, but fortunately, there are third party libraries that utilize the mentioned tools and expose the prop <rte-code>callbackNode<rte-code>. It’s an <rte-code>Animated.Value<rte-code> that's derived from specific gesture behavior. Its value range is usually from 0 to 1, which follows the progress of the gesture. You can interpolate the values to animated elements on the screen. A great example of the libraries that expose <rte-code>CallbackNode<rte-code> are <rte-code>reanimated-bottom-sheet<rte-code> and <rte-code>react-native-tab-view<rte-code>.

Giving your JS operations a lower priority for smoother animations

It is not always possible to fully control the way animations are implemented. For example, React Navigation uses a combination of <rte-code>React Native Gesture Handler<rte-code> and <rte-code>Animated<rte-code> which still needs JavaScript to control the animation runtime. As a result, your animation may start flickering if the screen you are navigating to loads a heavy UI. Fortunately, you can postpone the execution of such actions using <rte-code>InteractionManager<rte-code>.

This handy helper allows long-running work to be scheduled after any interactions/animations have completed. In particular, this allows JavaScript animations to run smoother.

Note: In the near future, you'll be able to achieve similar behavior with React itself on a renderer level (with Fabric) using the <rte-code>startTransition<rte-code> API.

In practice, you can show a placeholder, wait for the animation to finish, and then render the actual UI. It would help your JavaScript animations to run smoother and avoid interruptions by other operations. It's usually smooth enough to provide a great experience.

Remember that using JS-driven animations is bound to have significantly worse performance than native run on UI thread. Making it hard to achieve max FPS available for the device. Strive for platform-native animations whenever possible. And treat APIs such as <rte-code>InteractionManager<rte-code> for cases where heavy UI updates may interfere with gestures or animations. They might fight for the same resource-native UI thread.

Let users enjoy smooth animations and gesture-driven interface at 60FPS

There’s no one single right way of doing animations in React Native. The ecosystem is full of different libraries and approaches to handling interactions. The ideas suggested in this article are just recommendations to encourage you to not take the smooth interface for granted.

What is more important is painting that top-down picture in your head of all interactions within the application and choosing the right ways of handling them. There are cases where JavaScript-driven animations will work just fine. At the same time, there are interactions where native animation (or an entirely native view) will be your only way to make it smooth.

With such an approach, the application you create will be smoother and snappy. It will not only be pleasant to use, but also to debug and have fun with it while developing.

FAQ

No items found.
React Galaxy City
Get our newsletter

By subscribing to the newsletter, you give us consent to use your email address to deliver curated content. We will process your email address until you unsubscribe or otherwise object to the processing of your personal data for marketing purposes. You can unsubscribe or exercise other privacy rights at any time. For details, visit our Privacy Policy.

Callstack astronaut
Download our ebook

I agree to receive electronic communications By checking any of the boxes, you give us consent to use your email address for our direct marketing purposes, including the latest tech & biz updates. We will process your email address and names (if you have entered them into the above form) until you withdraw your consent to the processing of your names, or unsubscribe, or otherwise object to the processing of your personal data for marketing purposes. You can unsubscribe or exercise other privacy rights at any time. For details, visit our Privacy Policy.

By pressing the “Download” button, you give us consent to use your email address to send you a copy of the Ultimate Guide to React Native Optimization.