How to Optimize Your Android App Startup Time With Hermes

Well-optimized performance and running at the maximum possible speed are some of the most important factors contributing to the success of mobile applications. They greatly influence how your app is perceived by users and may significantly impact its ROI. What's more, well-performing apps achieve higher metrics in the Google Play Store, which raises the ranking in in-store searches. In this article, we will show you how to achieve all these benefits with the help of Hermes.

Achieve a better performance of your apps with Hermes

Issues: You’re loading a lot of Android packages during the startup time, which is unnecessary. Also, you’re using an engine that is not optimized for mobile.

Users expect applications to be responsive and load fast. Apps that fail to meet these requirements can end up receiving bad ratings in the App Store or Play Store. In the most extreme situations, they can even get abandoned in favor of their competition.

There is no single definition of the startup time. It's because many different stages of the loading phase can affect how “fast” or “slow” the app feels. For example, in the Lighthouse report, there are eight performance metrics used to profile your web application. One of them is Time to Interactive (TTI), which measures the time until the application is ready for the first interaction.

The loading process step by step

There are quite a few things that happen from the moment you press the application icon from the drawer for the first time.

a graph showing the loading process with a native initialization

The loading process starts with a native initialization which loads the JavaScript VM and initializes all the native modules (1 in the above diagram). It then continues to read the JavaScript from the disk and loads it into the memory, parses, and starts executing (2 in the above diagram).

In the next step, React Native starts loading React components and sends the final set of instructions to the UIManager (3 in the above diagram). Finally, the UIManager processes the information received from JavaScript and starts executing the native instructions that will result in the final native interface (4 in the above diagram).

As you can see in the diagram below, there are two groups of operations that influence the overall startup time of your application.

a graph showing two groups of operations that influence the overall startup time of your app

The first one involves the first two operations (1 and 2 in the diagram above) and describes the time needed for React Native to bootstrap (to spin up the VM and for the VM to execute the JavaScript code). The other one includes the remaining operations (3 and 4 in the diagram above) and is associated with the business logic that you have created for your application. The length of this group is highly dependent on the number of components and the overall complexity of your application.

This article focuses on the first group  –  the improvements related to your configuration and not the business logic itself. If you have not measured the overall startup time of your application or have not played around with things such as Hermes yet - keep on reading.

Long startup times and slow UX on Android can be one of the reasons your app gets a bad rating and ends up being abandoned

Creating applications that are fun to play with is extremely important, especially considering how saturated the mobile market already is. Now, all mobile apps have to be not only intuitive, they also should be pleasant to interact with.

There is a common misconception that React Native applications come with a performance trade-off compared to their native counterparts. The truth is that with enough attention and configuration tweaks, they can load just as fast and without any considerable difference.

How to improve your app’s performance with Hermes

While a React Native application takes care of a native interface, it still requires JavaScript logic to be running at runtime. To do so, it spins off its own JavaScript virtual machine. Until recently, it used JavaScript – Core (JSC). This engine is a part of WebKit –which powers the Safari browser – and by default is only available on iOS. For a long time, it made sense for React Native to use JSC for running JavaScript on Android as well. It's because using the V8 engine (that ships with Chrome) could potentially increase the differences between Android and iOS, and make sharing the code between the platforms way more difficult.

JavaScript engines need to perform various complicated operations. They constantly ship new heuristics to improve the overall performance, including the time needed to load the code and then execute it. To do so, they benchmark common JavaScript operations and challenge the CPU and memory needed to complete this process.

Most of the work of developers handling the JavaScript engines is being tested against the most popular websites, such as Facebook or X. It is not a surprise that React Native uses JavaScript differently. For example, the JavaScript engine made for the web doesn't have to worry much about the startup time. The browser will most likely already be running at the time of loading a page. Because of that, the engine can shift its attention to the overall CPU and memory consumption, as web applications can perform a lot of complex operations and computations, including 3D graphics.

The JavaScript virtual machine consumes a big chunk of the app's total loading time. Unfortunately, there is little you can do about it unless you build your own engine. That's what the Meta team ended up doing.

Solution: Turn on Hermes to benefit from a better performance

Meet Hermes - a JavaScript engine made specifically with React Native in mind. It is optimized for mobile and focuses on relatively CPU-insensitive metrics, such as application size and memory consumption. Chances are you’re already using it! As of v0.70, React Native has been shipping with Hermes turned on by default, which marks an important milestone in the engine’s stability.

It has come a long way from the bare-bones Android-only engine open-sourced in 2019, with a carefully curated set of supported JS features, through finding low-size-footprint ways of adding more EcmaScript spec features like Proxies and Intl, until making it available for macOS and iOS. Today Hermes is still small enough (~2 MB) to provide significant improvements to apps’ TTI and gives us a set of features rich enough to be used in most of the apps out there.

Before we go into the details of enabling Hermes in existing React Native applications, let’s take a look at some of its key architectural decisions.

Bytecode precompilation in Hermes reduces time to execute business logic

Typically, the traditional JavaScript VM works by parsing the JavaScript source code during the runtime and then producing the bytecode. As a result, the execution of the code is delayed until the parsing completes. It is not the same with Hermes. To reduce the time needed for the engine to execute the business logic, it generates the bytecode during the build time.

Bytecode precompilation with Hermes

It can spend more time optimizing the bundle using various techniques to make it smaller and more efficient. For example, the generated bytecode is designed in a way so that it can be mapped in the memory without eagerly loading the entire file. Optimizing that process brings significant TTI improvements as I/O operations on mobile devices tend to increase the overall latency.

No JIT in Hermes translates into increased TTI

The majority of modern browser engines use just-in-time (JIT) compilers. It means that the code is translated and executed line by line. However, the JIT compiler keeps track of warm code segments (the ones that appear a few times) and hot code segments(the ones that run many times). These frequently occurring code segments are then sent to a compiler that, depending on how many times they appear in the program, compiles them to the machine code and, optionally, performs some optimizations.

Hermes, unlike the other engines, is an AOT (ahead-of-time) engine. It means that the entire bundle is compiled to bytecode ahead of time. As a result, certain optimizations that JIT compilers would perform on hot code segments are not present.

On one hand, it makes the Hermes bundles underperform in benchmarks that are CPU-oriented. However, these benchmarks are not really comparable to a real-life mobile app experience, where TTI and application size take priority. On the other hand, JIT engines decrease the TTI as they need time to parse the bundle and execute it in time. They also need time to “warm up.” Namely, they have to run the code a couple of times to detect the common patterns and begin to optimize them.

How to start using Hermes on Android and iOS

If you want to start use Hermes, make sure make sure to turn the enableHermes flag in android/app/build.gradle to true:

project.ext.react = [
entryFile: "index.js",
enableHermes: true
]

For iOS, turn the hermes_enabled flag to true in ios/Podfile:

use_react_native!(
:path => config[:reactNativePath],
# to enable hermes on iOS, change `false` to `true` and
then install pods
:hermes_enabled => true
)

In both cases, whenever you switch the Hermes flag, make sure to rebuild the project according to the instructions provided in the native files. Once your project is rebuilt, you can enjoy a faster app boot time and likely a smaller app size.

Note: You’ll find more insights on compiling Hermes and integrating it with React Native on our blog. If you prefer to watch or listen, we also have an entire episode of The React Native Show podcast dedicated to Hermes, so be sure to check it out.

Improved startup time means better performance and UX

Making your application load fast is an ongoing effort and its final result will depend on many factors. You can control some of them by tweaking both your application's configuration and the tools it uses to compile the source code.

Turning Hermes on is one of the things you can do today to drastically improve certain performance metrics of your app, mainly the TTI. Apart from that, you can also look into other significant improvements shipped by the Meta team. To do so, get familiar with their write-up on React Native performance. It is often a game of tiny and simple improvements that make all the difference when applied at once. The React Native core team has created a visual report on benchmarking between stock RN and Hermes-enabled RN, so you might want to check it out.

As we have mentioned in the article on running the latest React Native, Hermes is one of those assets that you can leverage as long as you stay up to date with your React Native version. Doing so will help your application stay on top of the performance game and let it run at maximum speed.

The future with static Hermes (experimental)

At the React Native EU 2023 conf, the lead Hermes engineer, Tzvetan Mikov, announced an experimental tool that his team works on codenamed “Static Hermes.” It pushes what Hermes can do today with the ability to statically compile typed JavaScriptcode (essentially TypeScript or Flow) into native assembler instructions ahead of time. It exercises the idea that your app does not need to use Hermes or any other JavaScript engine to run because it already has the native code inside it. That's pretty wild.

Static Hermes is an ongoing experiment, however, you can give it a try today. Read more on Hermes' GitHub issue tracker: “How to try Static Hermes.”

Need help with performance? Give us a shout!

If you’re struggling with improving your app performance, don’t hesitate to contact us. We’re the official Meta and Vercel partners, and we’ve been delivering high-quality solutions for our clients and contributing greatly to the React Native ecosystem for a while. If you’re looking for a performance optimization partner, our React Native app development company is surely the right one.

Insights

Learn more about Hermes

cover

Dec 18Article

Why You Don’t Have to Minify JavaScript Code in React Native Apps

React Native skips JavaScript minification in production because Hermes handles dead code elimination at the bytecode level. Here’s how and why it works.

cover

Dec 4Podcast

From Static Hermes to Hermes V1: The Road to Default

Nicola Corti (Meta) explains the evolution of Static Hermes into Hermes V1, the performance boosts it ships, and the (experimental) path to making it the new default engine.

cover

Nov 6Live Stream

Hermes V1: What It Is, What It Isn’t, and What’s Next

Łukasz Chludziński, Szymon Chmal, and Matt Hargett unpack Hermes V1, covering what’s new, what’s changing, and what’s ahead for React Native performance.

cover

Jun 14Article

Technical Guide, Part 2: Integrating Hermes With React Native

Interested in the Hermes engine? Check the second part of our expert guide to find out how to integrate Hermes with React Native.

cover

May 26Article

Technical Guide, Part 1: Compiling Hermes for Apple Platforms

Want to use Hermes in your React Native app on iOS? Find out how to compile Hermes for Apple platforms. Check the first part of our expert technical guide.

cover

May 11Podcast

React Native 0.64 With Hermes for iOS

Discover how Hermes boosts React Native performance on iOS and macOS, with insights into AOT, bytecode precompilation, and the future of Hermes support.

cover

Apr 21Article

Hermes Performance on iOS

Wondering how the Hermes engine performs on iOS? We tested it on a React Native mobile app. Check the results and see how it compares to JSC.

cover

Apr 14Article

Bringing Hermes to iOS in React Native 0.64

Hermes is now available for iOS as well. Find out more about this JS engine - check the first part of our series on bringing Hermes to iOS.

cover

Mar 19Tutorial

The Hard Way vs. The React Native AI SDK Way: Stop Writing Custom Modules for Every Model

Learn how React Native AI uses a unified provider model to switch between cloud and on-device LLMs without rewriting application logic, enabling reliable AI experiences online and offline.

cover

Mar 12Tutorial

Build Smarter Apps: Tool Calling & AI Orchestration Explained

Learn how on-device models in React Native can call tools, interact with external APIs, and return structured outputs you can use directly in application logic.

cover

Mar 5Tutorial

MLC LLM + React Native: On-Device AI Without the Pain

Learn how to run third-party on-device LLMs in React Native using MLC LLM. Choose your models, run them efficiently across platforms, and keep a unified JavaScript API with React Native AI.

cover

Feb 24Tutorial

Intelligent Fallbacks with Apple Intelligence in React Native

Learn how to use Apple’s built-in Foundation Models in React Native apps, reduce memory usage, and enable fast, fully on-device AI with React Native AI Apple.

cover

Feb 10Tutorial

What Is the React Native AI SDK? A Complete Intro & Quickstart

Learn why on-device AI matters for React Native apps, how local LLMs behave offline, and what problems React Native AI is designed to solve from day one.

cover

Jan 29Tutorial

Implementing an Android TurboModule with Kotlin

Learn how to implement a React Native TurboModule on Android, from the TypeScript spec and Codegen to the Kotlin implementation and package registration.

cover

Jan 19Tutorial

Working with Different Threads in Swift TurboModules

Learn how JavaScript, native module, background, and main threads interact in React Native TurboModules, and how to use GCD queues to run async work safely in Swift.

cover

Jan 9Tutorial

How to Add Type-Safe Constants to Swift TurboModules

Learn how to expose typed, immutable constants from a Swift TurboModule using Codegen, and get full end-to-end type safety between native and JavaScript.

cover

Dec 30Tutorial

Adding Event Emitters to Your TurboModule in Swift

Learn how to emit native events from a Swift TurboModule and subscribe to them in JavaScript, including proper memory management and Codegen integration.

cover

Dec 22Tutorial

Writing Your First TurboModule in Swift

Learn how to build a fully working TurboModule in Swift, integrate it with Codegen, and bridge it through Objective‑C inside an Expo app.