Measuring and improving React Native apps startup time

Users expect apps to be responsive and fast to load. An app with a slow start time doesn’t meet this expectation, and can be disappointing to users. This sort of poor experience may cause a user to rate your app poorly or even abandon your app altogether.

Google's recommendation is to have your app startup in less than 5 seconds.

There's not a lot of studies done to measure the impact of improving startup time in the context of a mobile app. In this study, the author shows that improving startup time by 53% improved the ratings of the app, and improved conversion rate by 2.2%.

We could hypothesize that the situation is similar to the web. For instance, Amazon found that every 100ms of latency cost them 1% in sales.

Measuring

Measuring the startup time of a React Native app is not as straightforward as reading the vitals on the Play Store. Indeed, the play store reports only the native startup time, but the app then needs to initialize the JavaScript and run it before being actually responsive.

One way to measure time lapses locally and in production is with Firebase Performance.

A startup trace with Firebase performance

To have a neat graph like the one above on your project, you should:

1. Install Firebase Performance with react-native-firebase
2. Use react-native-startup-trace and follow the usage part of the documentation to add a measure for the JavaScript startup

USE HERMES

The most important step you need to take is moving to Hermes. Here's a glimpse of the difference it can make on lower end Android devices (here on a Wiko Fever 4G): Hermes - before and after gif

3.9s to load with Hermes vs 12.9s without Hermes. 🚀

 


This article by the FB team explains well why the impact is so massive, especially with the GIF below:

Bytecode precompilation with Hermes

Basically, React Native used to come with a disadvantage for large apps in terms of startup performance. In our app for instance, we noticed that our APK included 3MB of JavaScript which needed to be parsed, compiled and executed at the start of the app.

If you want to take a look at how much JavaScript you have in your app, you can download the APK file and unzip it (use unzip myapp.apk on MacOS). The JavaScript file is located at assets/index.android.bundle.

If you use Hermes however, JavaScript is no longer packaged with your app. Hermes already does the parsing and compiling into bytecode at build time, which means your index.android.bundle file will contain bytecode and not JavaScript (try to open it after switching to Hermes to see the difference), which will be much faster to start.

 

How to INSTALL it

Enabling Hermes is simple and can be done following those two steps

However it can come with a bit of gotchas to double-check:

1. Proxy support is included in React Native 0.64 and above. It is used in dependencies such as immer, or @react-native-firebase/database

2. Intl support will be included in React Native 0.65 and above. It is used in dependencies such as lingui (for pluralization), date-fns-tz

3. Hermes implements the JavaScript norm ES6. If you use TypeScript, make sure you target "lib": ["es6"] in the tsconfig.json. It could spot some issues for you

4. If you're using crash reporting (Sentry for instance) with custom build types (for AppCenter deployment for instance), there's an issue with crash reporting source maps on app center builds that can be fixed with this PR

5. CodePush users: Hermes add a step when deploying with codepush: 1st Step: Bundle JS > 2nd Step (new): Convert to Hermes Bytcode , only the new version from appcenter-cli supports this. Make sure to test a deployment with Codepush. Source

6. If your project has a different folder structure than the React Native default structure, you need to override hermesCommand in your app.build.gradle config as it is done here

Once all this is done, make sure to test the performance gains on a release build! In our experience, Hermes was actually slower in debug mode, but much faster in release builds.BAM's apps - before and after Hermes

We migrated 4 apps to hermes. on average, startup time improved by 53%

Other improvements to consider

Simplify your splashscreen

If you're using react-native-splash-screen, and you have a complex layout in your splashscreen, this could also be a cause. Experiment to check the difference between when the splashscreen is shown and when it's not.

For instance, our app had a complex Lottie animation in the splashscreen which was adding ~1.5s just to load the animation on the start up time.

Move some initial logic to the native side

If you rely on some initial logic before making your app responsive - an API call for instance - you can move it to the native side, and expose the result via a bridge to load the result in parallel of the JavaScript being loaded.

ENJOY YOUR RESULTS

Try it out for yourselves, move to Hermes and share your success stories! 🤩
How much startup time have you gained? 🚀