civil-and-structural-engineering
Using Hermes to Improve React Native App Performance on Android
Table of Contents
React Native has become a cornerstone for cross‑platform mobile development, enabling teams to ship apps quickly using JavaScript and React. But when it comes to Android, performance can be a real pain point. Sluggish startup times, higher memory consumption, and janky animations are common complaints. One of the most effective ways to address these issues is to replace the default JavaScript engine with Hermes. This open‑source engine, built by the React Native team, is designed specifically to make React Native apps faster and leaner on Android devices. In this article, we’ll dive deep into what Hermes is, how it works, and how you can enable it in your project to deliver a noticeably smoother user experience.
What Is Hermes?
Hermes is a JavaScript engine created by Facebook (now Meta) that was first released as an option for React Native in 2019. Unlike the standard JavaScript engines used in mobile apps—JavaScriptCore on iOS and V8 or JSC on Android—Hermes is purpose‑built for React Native. Its primary innovation is ahead‑of‑time (AOT) compilation. Instead of parsing and compiling JavaScript just‑in‑time (JIT) when the app runs, Hermes compiles your JavaScript source into efficient bytecode during the build phase. This bytecode is then executed directly by the Hermes runtime, eliminating the overhead of JIT compilation.
The result is faster startup times and a smaller memory footprint. Hermes also uses a compact bytecode format that reduces the overall size of your app bundle. Additionally, the engine is optimised for low‑end Android devices, where resource constraints are most noticeable. Because Hermes doesn’t rely on a JIT compiler, it also avoids the initial CPU spike that typically happens when an app starts.
Benefits of Using Hermes
Enabling Hermes in your React Native project delivers several measurable improvements:
- Faster App Startup: AOT compilation means there is no time spent parsing and compiling JavaScript at runtime. On many Android devices, this can cut startup time by 30–50%.
- Lower Memory Usage: Hermes uses a dedicated garbage collector that is optimised for mobile workloads. The engine also stores compiled code in a compact form, reducing both the resident memory and the overall heap.
- Smaller App Size: The bytecode format is significantly smaller than the raw JavaScript bundle. Reductions of 20–30% in APK size are common, which is particularly valuable for markets with limited storage.
- Smoother Interactions: Without JIT pauses, the runtime behaves more predictably. Animations and gestures spend less time blocked by garbage collection or compilation, leading to fewer frame drops.
- Better Battery Life: Less CPU time spent on compilation and less memory pressure mean the device can stay cooler and use less power during app usage.
- Improved stability on low‑end devices: Hermes’s memory‑savvy design helps avoid out‑of‑memory (OOM) crashes on phones with 2GB of RAM or less.
Hermes vs Other JavaScript Engines on Android
To appreciate Hermes, it helps to understand what you are replacing. Historically, React Native on Android used JavaScriptCore (JSC), the same engine that powers Safari. JSC is a well‑tested JIT engine, but on Android it is externally compiled and can be slow to start. It also consumes more memory because it keeps the JavaScript source in memory for JIT compilation. Some projects switched to V8 (the engine from Chrome), which offers excellent runtime performance but at the cost of a larger binary and even higher startup overhead.
Hermes takes a different approach. By forgoing JIT entirely and relying purely on AOT bytecode, it trades peak runtime speed for much faster startup and lower memory usage. In practice, the runtime performance of Hermes is still excellent for most React Native applications because the bulk of the heavy lifting happens in native modules, not in JavaScript. The net result is that your app feels snappier from the moment the user taps the icon.
It’s also worth noting that Hermes is now the recommended default for new React Native projects (starting with RN 0.70). The core team has invested heavily in ensuring compatibility and performance parity with JSC for modern JavaScript features.
How to Enable Hermes in Your React Native Project
Enabling Hermes is straightforward, especially if you are using React Native 0.60 or later. Below we walk through the steps for Android and also mention iOS configuration (since many React Native apps target both platforms).
Prerequisites
- React Native version 0.60 or higher (0.64+ is recommended for iOS Hermes support).
- A current version of Android Studio and the Android SDK.
- For iOS, Xcode 11 or later and CocoaPods.
Enabling Hermes on Android
Open the file android/app/build.gradle. Look for the project.ext.react block. If it doesn't exist, you can add it. Inside, set enableHermes: true:
project.ext.react = [
enableHermes: true, // false by default
]
If you are using React Native 0.70 or later, the project.ext.react block is no longer used. Instead, Hermes is controlled via the hermesEnabled option inside the defaultConfig block. Modify your android/app/build.gradle as follows:
defaultConfig {
...
hermesEnabled true
...
}
After making the change, clean and rebuild your project:
cd android
./gradlew clean
cd ..
npx react-native run-android
The first build may take longer because Hermes compiles your entire JavaScript bundle to bytecode during the build process.
Enabling Hermes on iOS (optional, but beneficial)
Although the article focuses on Android, many projects benefit from enabling Hermes on iOS as well. In iOS, you enable Hermes via the Pods configuration. Open your ios/Podfile and uncomment the Hermes line:
# :hermes_enabled => true
Then run pod install and rebuild. Keep in mind that on iOS, Hermes is still experimental in some configurations, but it has been production‑ready since RN 0.64.
Verifying That Hermes Is Running
Once your app is built and running, you can confirm that Hermes is active. In the Android Debug Log (using adb logcat) search for a line like:
I/Hermes: Initializing Hermes JavaScript engine
Alternatively, you can programmatically check:
import { Platform } from 'react-native';
if (Platform.OS === 'android' && global.HermesInternal) {
console.log('Hermes is enabled');
}
You can also use the Hermes Debugger in Chrome DevTools or Flipper to inspect bytecode execution.
Performance Testing and Benchmarking
Once Hermes is enabled, you should measure the improvement to validate your efforts. Here are practical ways to benchmark.
Startup Time Measurement
Use the React Native Startup Trace feature. In your app, wrap your root component with Performance.mark calls. Better yet, use Flipper with the React Native Performance Plugin to see the time spent in JavaScript before the first paint. Compare builds with Hermes on and off to see the reduction.
Memory Profiling
Open Android Studio’s Profiler while running your app. Look at the Memory graph. Hermes should show a lower baseline and fewer spikes. You can also capture heap dumps and inspect object counts.
Hermes Sampling Profiler
Hermes ships with a built‑in sampling profiler. You can activate it via the React Native Dev Menu (shake the device) and selecting “Show Hermes Profiler”. This generates a Chrome DevTools‑compatible trace. Run your app through typical user flows and compare the trace with and without Hermes to see where time is spent.
Troubleshooting Common Issues
While Hermes is robust, you may encounter a few bumps. Here are the most common and how to fix them.
“Hermes engine not enabled” warnings
If you see a yellow warning box telling you that Hermes is not enabled, it means the JavaScript engine did not start up as expected. This often happens if you didn't rebuild the native code or if there is a mismatch between the RN version and the Hermes version. Clean and rebuild: npx react-native run-android with the --reset-cache flag.
ABI Filter Issues
Hermes pre‑built binaries are included for the four main Android ABIs (armeabi‑v7a, arm64‑v8a, x86, x86_64). If you are building an app that targets only a subset (e.g., only arm64), make sure your android/app/build.gradle does not exclude the Hermes native library via abiFilters incorrectly. A typical configuration:
splits {
abi {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
}
ProGuard / R8 Rules for Release Builds
When you generate a release APK, ProGuard or R8 may obfuscate or remove some Hermes‑referenced classes. Add these rules to your proguard-rules.pro:
-keep class com.facebook.hermes.** { *; }
-keep class com.facebook.jni.** { *; }
Also ensure that your android/app/build.gradle does not enable minifyEnabled without the corresponding keep rules.
Features That Are Not Fully Supported
Hermes deliberately does not support eval, the Function constructor, or Proxy for performance and security reasons. If your codebase uses any of these, you will see runtime errors like Cannot call 'eval' with Hermes!. You’ll need to refactor such code—most commonly replaced by alternatives that do not rely on dynamic code execution.
Also, features like RegExp lookbehind assertions ((?<=...)) are not supported. Check the Hermes compatibility table in the official documentation.
Best Practices with Hermes
To get the most out of Hermes, follow these guidelines.
- Avoid dynamic code execution: Remove uses of
eval,new Function(), andrequirewith non‑static strings. Pre‑compile template strings and use static imports. - Use modern JavaScript syntax: Hermes fully supports ES2020 + some ES2021 features. Write clean modern code and let Hermes compile it efficiently.
- Enable Fast Refresh and HMR: Hermes works with Fast Refresh without issues. During development, hot reloads are even faster because the bytecode is cached.
- Use code splitting: Hermes supports lazy loading through
React.lazyandSuspense, but be careful with dynamic imports—they require additional configuration (e.g., Metro’sexperimentalImportSupport). - Profile regularly: Use the Hermes sampling profiler and Flipper to identify performance bottlenecks. Even with Hermes, poorly written JavaScript can still cause jank.
- Consider the New Architecture: React Native’s New Architecture (Fabric + TurboModules) leverages JSI, which is compatible with Hermes. Using both together yields even better performance for inter‑op with native modules.
Conclusion
Hermes has matured into a powerful tool that directly addresses the performance shortcomings of React Native on Android. By switching to this AOT‑compiled engine, you can expect faster startup times, lower memory usage, smaller APK sizes, and a more predictable runtime. The setup process is simple, and the benefits are especially visible on mid‑range and low‑end devices where every millisecond and kilobyte counts.
If you are starting a new React Native project today, Hermes is enabled by default—so you get these benefits for free. For existing projects, the migration is painless and should be a priority. Combined with good coding practices and regular profiling, Hermes allows you to deliver a native‑quality experience with React Native’s productivity.
For further reading, check out the official React Native Hermes documentation, the Facebook Engineering blog post announcing Hermes, and a community performance benchmark comparison for deeper insights.