React Native Performance: Major issues and insights on improving your app’s performance

Mobility Product Engineering

React Native Performance: Major issues and insights on improving your app’s performance

React native seems to offer a promising solution for those who want a viable framework to develop cross-platform applications. Within a short span of time, it has surpassed the likes of other platforms like Xamarin and Ionic

However since it’s a relatively new entrance among other cross-platform development frameworks, it’s not immune of the performance issues. The more tabs, navigation, controls, animations and third-party libraries your app has, the slower React Native becomes. Each change that you make, gets implemented from one React Native build to another. In this article, we will talk about React Native performance, it’s cause and the way to eliminate the common performance issues in your React Native app development.

React Native Performance – What’s the real cause behind performance issues?

In order to understand the root of issues in React native performance, we must understand how React Native works under the hood. React Native leverages 3 main parts to run the framework: Native Thread, Javascript thread and the bridge. Here’s the actual significance and meaning of these threads:

  • Native thread built for running Java/ Kotlin, Swift/ Objective C
  • Javascript thread which runs everything from javascript based animations to other UI components
  • Bridge which acts as an intermediate communication point for the native and JS thread

One key element to understand the real cause behind React Native performance issues is that each thread (i.e Native and JS thread) is blazingly fast. The performance bottleneck in React Native app occurs when you’re passing the components from one thread to other unnecessarily or more that the required. A major thumb rule to avoid any kind of performance related issue in React Native is to keep the passes over the bridge to a minimum. 

Using React Native Slowlog to optimize performance issues in React Native 

Now that we learned the real cause of React Native performance limitations, let’s understand how can we dig out the performance issues in a React Native app with the help of a tool. React Native Slowlog does wonders when it comes to view rendering performance in your app. The tool let you to add a custom threshold with which you can build your baseline for future performance improvements. All you need to do is to add following  line of code in your views constructor and start with React Native perf profiling: 

slowlog(this, /.*/)

Common React Native performance issues in Android

Memory leak in Android lists

There are a couple of reasons for which memory leaks can be induced in your react native app. Take the case of scrolling in an hybrid android music application for example. On scrolling down the list of songs up to the 3rd page, the app starts freezing up RAM on mobile and eventually degrades performance.Since both operations occur on different parts(one in native, other in Javascript or React native part), passing this information between these two parts happens over a bridge and consumes considerable time.

Solution: Fixing memory leak due to listview

One way of fixing this is by using FlatList or VirtualizedList instead of using ListView. Using FlatList in React Native is particularly awesome when you:

  • Needs building Infinite scroll pagination
  • Needs building Pull to refresh
  • Needs smooth rendering performance

While FlatList performance in React Native is simply astounding for the majority of use-case, developers also use an alternative high performing interface to build better scroll experience known as SectionList

Application size and performance on Android

An increased application size could have a drastic effect on your app’s performance. This is common problem among android apps which heavily reply on third party sources, multiple screens, libraries, etc to operate. These added resources have a direct impact on the application size, increasing the size.

To optimize the application size of a react native application, you should:

  • Use Proguard to minimize the application size.
  • Create reduced sized APK files for specific CPU architectures. When you do that, you app users will automatically get the relevant APK file for their specific phone’s architecture. This eliminates the need to keep JSCore binaries that supports multiple architectures and consequently reduces the app size.
  • Compress images and other graphic elements. Another alternative to reduce image size is using file types like APNG in place of PNG files.
  • Don’t store raw JSON data. Compress it or convert it into static object IDs.
  • Optimize native libraries.

React Native navigation components in Android

Ever since the launch of React Native, its core development team had to experiment a lot on navigation to remove inconsistencies between JS and native thread bridge overlay. There were multiple React Native performance issues associated with navigation that needed optimization. 

If you ever wondered about the navigational difference between React Native and Native screens, here’s a GIF that explains the difference visually:

React Native vs Swift comparison user experience

If you wish to explore these navigation solutions deeply, go through below section. Else, you can skip to the next one!

Navigator

Navigator is a pure JS navigation component. Though, many developers find it confusing as sometimes it is not that powerful in terms of performance.

For instance: OurSky, a web development company based in Taiwan had to ditch Navigator component for their first client project as it is not suitable in cases when you have to keep a common navigation code between Android and iOS.

The main performance issue comes out in their case when they had to add more features to the app due to which multiple operations fall on the JS thread.

So, they choose ReactNavigation, which works well with redux and provided them an improved performance satisfaction.

NavigatoriOS

NavigatoriOS was mainly designed as native for IOS. So, the actual problem started when Android comes into the picture. This limits it suitable only for the cases when you are working around with iOS as a platform.

NavigationExperimental

NavigationExperimental was designed as a highly configurable navigator but it was more confusing for most developers to understand and grasp.

It was introduced as a promising solution for the react native navigation problem but it certainly failed to solve AirBnB’s navigation problem.

Navigation components mentioned above can still work on your app, given that your app is based on simpler UI and not much-complicated operations.

The major problem will be encountered when your app requires the native feel and functionality which only NavigatorIOS can give you (provided that your app is based on iOS).

React Navigation

A much better solution for navigation in react native is React Navigation which boasts of a customizable JS re-implementation of the native views.

React Navigation provide routers that come very handy when you need to put your navigation logic into redux. It can be your alternative solution to all the above navigation components.

Rendering Native views faster

Moreover, developers have to take advantage of React Native’s Virtual DOMfeature.

Virtual DOM allows you to optimize the JS rendering components, and batch it asynchronously with their diff algorithm. This effort minimizes the amount of data being sent over the bridge and improves the rendering and syncing of both the Realms.

This algorithm was first built for Javascript based web applications, but many applications including UberEat and F8 have used it to optimize the messageQueue and improve their app’s performance.

Delay in updating UI in Android

As already mentioned above, React native offloads all complex functions to the Javascript thread which causes a delay in updating the app’s user interface.

Under heavy loads and complex operations, such offloading fails to maintain app’s performance. And when that happens, the app stop responding to user inputs and performance lags are extremely apparent. The same offloading can drastically impact animations from executing. The main javascript thread if isn’t free, navigation and layout related animations won’t launch. React Native’s team has promised to move animations to the main thread to improve the performance.

Improving React Native app Launch time

A slow app launch time can left you with only few inactive users. It’s the biggest factor contributing to user churn. A major source of slow app performance and delays on launch times can be attributed to the default implementations of React Native. The first thing we would suggest you do to improve app’s launch time is to take care of Object.finalize element. Finalizers run on a single thread, so every other object has to wait until all finalizers have passed through, before they can be garbage collected. This creates huge dependencies which in turn lead to slow app launch time. 

Image caching in React Native for Android

Though there are some npm libraries that attempts to solve the image caching issue in android by implementing caching logic in JS and storing images on file system. But, they often fail to provide the optimal performance we desire to deliver a delightful app experience.

The challenges most of these libraries bring to image caching on Android are:

  • Cache miss: Whenever an app’s page is refreshed, often these libraries fail to fetch previously loaded images.
  • Performance issues: Performance lags when the caching logic is ran on the javascript side of application

Looking at other alternatives, react-native-cacheable-image brings dynamic caching with placeholder based implementations. Another improved version of this library can be found with react-native-cached-image.

React Native Image Optimization on Android

When you work around in react native, it is very important to use responsive images. Otherwise, you may find your with an app that delivers sub par performance and multiple error messages.

Zeemee engineering team while developing their app with react native discovered that photos in the user profile sections would constantly flash.

React native image optimization

Here’s an overview of lessons learned through Zeemee’s Android experience on React Native:

  • Use smaller images: This happened because they are using larger images due to which OS would load the image for some time and then remove it while reloading other. To fix this, they had to request smaller images from the server.
  • Use PNG as opposed to JPG: Using PNG files as a static image of your react native app will also lead you to the problem of memory leaking. This is because react native use fresco library to render and display images. This can be solved by using JPG images which will reduce the memory footprint as a result.
  • Convert your images to WebP format: Another alternative fix to this issue is to convert your images into WebP format. WebP format support both lossy and lossless compression techniques which are best for compressing the size of images. WebP images can speed out your image loading time to 28%.

React Native app performance : Evaluating performance issues in iOS

Animating the size of images for different UI view

In React Native applications, every time a user adjust the width or height of an image, it is re-cropped and scaled from the original image size. This process consumes a lot of memory and the app takes few seconds to load, especially for large images.

To tackle these changes, most developers use transform: [{scale}]  style property to dynamically fit the image sizes for different UI view. Moreover, to make it more memory efficient, you have to implement a custom FadeInImagecomponent to display a tiny shade.

In this approach, React Native applications can quickly project images for different UI view using the onLoad component and the cached fade in image.

Multi-threading issues in React Native

Going back to javascript threads, React Native doesn’t supports multiple threads (small unit of process) at the same time – This is also known as multi-threading. When React Native is rendering one component, other components have to wait till the first one is rendered. Twitch had to move away from React Native because of these challenges. When they started implementing live chat feature that runs along parallel to a live video feed, they faced major performance bottlenecks. This impacted low-end devices even more.

react native performance with live video and chat Twitch
Credits: Twitch.tv

Build your own extension code to take care of multi-threading in React Native.

While the support for multi-threading isn’t there, we have found writing our own extension code with heavy focus on system design and maintainability a viable solution. You can easily write an extension code that creates a bridge between React native and native components. These extensions can be easily written using:

  • Java
  • Objective-C
  • Swift

Infinite Scrolling: performance optimization for React Native

React-Native-Infinite-Listview

Default implementation of Infinite scrolling in React native is one of the biggest performance bottleneck for newsfeed type of applications. React Native provides three ways to implement infinite scroll:

  • ScrollView: It renders all list/feed elements at once
  • ListView: Brings in additional rendering options(re-loading elements pre-defined)
  • FlatList: For react native versions > 0.43

Following are the common performance issues with infinite scroll options:

  • ScrollView: Loading all elements at once impacts memory usage and performance
  • ListView: Loads UI elements better than ScrollView, but isn’t good enough for highest possible performance
  • FlatList: Only load a UI component or a list item when it is needed by the user, better known as Lazy loading.

Chop, an ecommerce startup based of San Fransisco came out with a solution which was based of Brent Vatne’s experiment for Fixed height infinite list.They implemented a sliding window with the list in such a way that when the screen gets scrolled the sliding window moves up, but the items remain fixed at their respective positions. Since, the items are not moving hence the entire memory usage is of no issue here.

Progressive Image Loading in React Native iOS

Image Placeholders in React Native

Your app image gallery will require image placeholders in situations where there is any possibility of running it in a slow internet connection or in usability cases such as a news app or e-commerce app where you have larger image grids. In case if you aren’t aware about image placeholders in app’s, the below illustration would better help you:

react native image optimization: Placeholders

When you use react native image component then it will provide you a plain user experience which is not a good user experience.

To overcome this, organizations such as Facebook and Medium use progressive loading technique in mobile which is based on a simple idea as follows:

  • Use a tiny thumbnail to display on your placeholder – You can do this by returning the markup of this tiny thumbnail in the initial HTML as <img/>, so that it can be fetched by browser easily
  • Use the Progressive image component – Next, you can use your progressive image logic to load the thumbnail first and then fade it for sometime when the actual image is ready for displaying

The entire code for the progressive image loading is available on GitHub here.

Using cleaned console.log statements

Though console.log( ) statements are important to debug your react native app but these statements in huge numbers can cause performance lag in your react native app. The reason being that these pieces of code are synchronous so they might slow down your app. You can remove all the console calls by using babel plugin.

Handling Animations in React Native

React Native offers an animated API to build and run basic stock animations like layout transitions. But, to build custom animations you might be using third party libraries like this one.

When you use third-party APIs or libraries, the frame rate drastically slows down and so is performance. Moreover, in some cases, it drains the battery faster than the usual. One way of dealing with animations in react native is to ensure that the framerate of your app never or rarely dips below 60fps.

Following are some performance issues related to frame rate drop which generally occur while you play around animations in React Native:

Heavy computation work on JS thread

A frame will be usually dropped in cases when you are trying to do something heavily on computation. For example, when you render a new scene with multiple child components, there are stronger chances of a drop in the frames.

This can be fixed by using an Animated library with Native driver. With this approach, you can improve your serialization/ deserialization time. Moreover, the performance will improve drastically as you are doing most of the interpolation work in native thread

Slow navigator transitions

In react native, javascript thread controls navigator animations. Slow navigator transitions take place when react is trying to render new screen when the animation is running on Javascript thread. This locks up the javascript thread and slitters animations.

Using interaction manager will considerably improve your performance in cases like slow navigator transitions. However, if your user experience is suffering way more than expected, then you should better stick with Layout Animation.

Conclusion

In this article we have put together all major issues on React Native performance along with countermeasures to optimize them. About more than half of performance issues in React Native can be solved with little bit of workaround. Hope this shed some light on improving your app’s performance, and push your knowledge of the platform a little further. 

Working from last 8 years into consumer and enterprise mobility, Hardik leads large scale mobility programs covering platforms, solutions, governance, standardization and best practices.

  1. Mark Caple

    Thanks Purvak great set of rules to follow. Fortunately or unfortunately I had already decided on react-native-navigation. Hope we've chosen the right option. :)

    • purvak_simform

      Hey Mark, Thanks for kind words. I am certain that your engineering team can easily optimize for performance with react-native-navigation. Would to hear back on your journey with React native. Anyways, if you run into any problems, feel free to reach out. :)

  2. scarlz

    I would strongly have to disagree with any recommendation for using React Navigation, not least that it's the "best overall". It has fundamental design flaws, performs poorly, is missing too many core features, development is very slow, and long-standing issues/pull requests are ignored or outright closed without action. It simply is not production ready. I was lured into its use on the basis that, in being the official choice for navigation on React Native, I couldn't go far wrong with it. How wrong was I. You will spend more time fighting with / hacking around with that library only to produce sub-par results than anything you've ever known.

    • purvak_simform

      Hey @scarlz:disqus Agree with you on React Navigation, we were lured as well. In some cases, it worked wonders, in others - it didn't. That's why we listed alternatives to React Navigation. Anyways, I would love to learn about you experience with react native limitations? Care to share some of your bits on purvak@simform.com?

  3. sabtee

    What are some tools that you use to measure performance?

Your email address will not be published. Required fields are marked *

eCommerce web application

Get insights on building high performing, maintainable react native apps

Learn how you can improve your app's performance, increase speed to market and build apps that your customers love!

You have Successfully Subscribed!