skip to content

Hermes Engine

Hermes compiles JavaScript to bytecode at build time so launch skips parsing, and Hermes V1 is the default engine since 0.84. Interviewers probe its effect and what JSC's removal means.

part ofReact Nativeoverview, primer and where to startread it →
on this pageshow

explore

questions

4

What is Hermes in React Native, and why does it make a release build start faster than an engine that parses JavaScript source at launch?

level: juniorimportance: must knowfreq 72%

answer

  1. work moves from the phone to the build
  2. hermesc runs after Metro bundles
  3. bytecode replaces the bundle file
  4. launch skips parse and compile
  5. default since 0.70; V1 since 0.84

basics

~20 s

Hermes is React Native's default JavaScript engine. In release builds its compiler, hermesc, turns the Metro bundle into bytecode on the build machine, so at launch the device runs ready bytecode instead of parsing and compiling JavaScript source.

solid answer

~40 s

Hermes is the JavaScript engine React Native ships and uses by default (since 0.70; since **0.84** the default is **Hermes V1**). Its defining trick is **ahead-of-time compilation**: in a release build Metro produces the bundle, then the build runs `hermesc` on it and packages **Hermes bytecode** in place of the JavaScript (`index.android.bundle` on Android, `main.jsbundle` on iOS). An engine that receives source must read, parse and compile megabytes of JavaScript on every cold start before the first component renders; Hermes pays that cost once, on the build machine. The React Native docs cite faster start-up, lower memory use and a smaller app than JavaScriptCore. It does not make slow renders cheaper, and debug builds, which load plain source from Metro, show none of the gain.

code

bash · 3 lines
bash
# Build and run a release variant, where hermesc compiles the bundle to bytecode
npm run android -- --mode="release"
npm run ios -- --mode="Release"

go deeper

for a junior

Recall three facts: Hermes is React Native's default JavaScript engine, it compiles JavaScript to bytecode during the release build, and that is why the app starts faster.

for a middle

Walk through the pipeline: Metro bundles without minifying, hermesc compiles with -O, and the bytecode replaces index.android.bundle or main.jsbundle. Explain why debug builds do not show the gain.

for a senior

Show you know what the engine does and does not fix: startup parse cost and memory, not render cost. Insist on release-build measurements and know the 0.81 JSC removal and 0.84 Hermes V1 default when planning an upgrade.

for a principal

Frame the engine as a platform decision already made by React Native: core tests and ships Hermes V1, so the question is whether any dependency justifies leaving that path, not which engine to pick.

## What Hermes is **Hermes** is the open-source JavaScript engine that React Native builds and ships for you. It executes all of your app's JavaScript on the device: components, hooks, state libraries and business logic. It has been React Native's default engine since **0.70**, and since **0.84** the default is **Hermes V1**, a new generation of the Hermes compiler and virtual machine (VM) that the 0.84 release post credits with better execution speed and lower memory use. Apps already on Hermes got V1 with no configuration change. Each React Native release pins a matching Hermes: the engine binaries and the `hermes-compiler` npm package that provides the `hermesc` compiler (React Native 0.87.1 pins `hermes-compiler` 250829098.0.17). Keeping the two on one train matters because the bytecode a compiler emits must match the VM that runs it. The React Native docs summarise the benefit plainly: for many apps Hermes gives **improved start-up time, decreased memory usage and a smaller app size** compared with JavaScriptCore (JSC), the engine that used to be the alternative. ## Compile at build time, not at launch A classic JavaScript engine is handed **source text**. Before your first line runs it has to: 1. read the whole bundle into memory; 2. **parse** it into a syntax tree; 3. **compile** that tree into the engine's internal bytecode; 4. only then start executing. A React Native bundle holds your code plus React, React Native's own JavaScript layer and every library you import, often several megabytes. Steps 2 and 3 on every cold start are paid by every user on their phone's CPU, and cheaper phones pay the most. Hermes moves that work to the **build machine**. In a release build: 1. **Metro** bundles your JavaScript into one file. With Hermes the build scripts do not minify it, because bytecode compilation makes minification pointless. 2. The build runs **`hermesc`** with optimisation (`-O`, the default Hermes flag on both platforms) and produces **Hermes bytecode**, a `.hbc` file. 3. The bytecode takes the bundle's place in the package: the Android Gradle plugin's release bundle task moves the `.hbc` over `index.android.bundle`, and the iOS build phase writes the compiled output to `main.jsbundle`. At launch the engine loads bytecode that is ready to run. There is no source to parse and nothing to compile, and that skipped work is where the startup gain comes from. ## What this buys, and what it does not - **Shorter time to the first screen**: the parse-and-compile phase disappears from the cold-start path. - **Lower memory**: React Native's docs note that bytecode is loaded into memory on demand and never has to be parsed, and they report decreased memory usage for many apps. - **A cost paid once**: the compiler runs on the build machine, not on each user's device at every launch. - **Not a faster app in general**: Hermes does not make an expensive render, a heavy effect or a badly configured list cheaper. Those are separate problems with separate fixes. - **Not in a debug build**: debug builds normally fetch JavaScript from the Metro dev server as plain development-mode source, which the engine compiles on the device. A startup time measured in debug says nothing about release. | | Debug build | Release build | |---|---|---| | Where the JavaScript comes from | Metro dev server (normally) | The bundle packaged in the app | | What the engine receives | Development-mode JavaScript source | Hermes bytecode compiled with `-O` | | Where compilation happens | On the device, at load | On the build machine | | Useful for judging startup | No | Yes | ## Where the engine story stands in React Native 0.87 - **0.70**: Hermes becomes the default engine. - **0.79**: JSC begins moving out of core into the community package `@react-native-community/javascriptcore`. - **0.81**: the built-in JSC is removed; an app that needs JSC must use the community package. - **0.82**: Hermes V1 arrives as an experimental opt-in. - **0.84**: Hermes V1 becomes the default on iOS and Android. - **0.87**: the build tooling treats Hermes V1 as always on and ignores the old opt-out flag with a warning. Expo SDK 57 apps run React Native 0.86, so they are on Hermes V1 as well. ## The interview answer in one line Hermes is fast to start because it is **ahead-of-time**: the expensive parse and compile phase runs once, in the release build, and devices only execute bytecode. Name the tool (`hermesc`), the artefact (bytecode replacing the bundle), the condition (release builds) and the current default (Hermes V1 since 0.84), and you have covered what interviewers listen for.

  • Why do React Native's build scripts skip minifying the bundle when Hermes is enabled?
    Minification shrinks source text so it downloads and parses faster. With Hermes the device never parses the bundle at all: `hermesc` compiles it to bytecode on the build machine, so a minification pass buys nothing. The Android Gradle plugin sets minification to the opposite of the Hermes flag, and the iOS build script passes `--minify false` for Hermes release builds, noting that Hermes does not require minification.
  • Does Hermes also make re-renders and list scrolling faster?
    Not directly. Build-time bytecode removes parse and compile work from launch. Once the app is running, the cost of rendering is the cost of your components, props and list configuration, whichever engine executes them. Hermes V1 does bring general execution-speed and memory gains, but an expensive render is fixed in the component, not by the engine choice.

A flat-pack wardrobe versus one delivered assembled: a source-loading engine assembles the furniture in your living room every time you move in, while Hermes has it assembled at the factory, so the only job left at your door is carrying it in.

saying these in an interview costs you the question

  • Hermes compiles JavaScript on the device the first time the app launches
  • Metro produces the Hermes bytecode and streams it to the app
  • Hermes makes every render and list scroll faster
  • Startup measured in a debug build shows Hermes's gain
  • React Native still defaults to JavaScriptCore on iOS
  • Hermes V1 is still an opt-in you enable in gradle.properties
open as a page

In React Native, how do you confirm Hermes is running, and why can that check pass while a transit-timetable app still cold-starts slowly?

level: middleimportance: should knowfreq 38%

basics

~20 s

Check the HermesInternal global, which only Hermes defines. It proves the engine, not the input: debug builds run development source from Metro, and non-standard bundle loading can skip the precompiled bytecode, so measure a release build that ships the .hbc.

open as a page

In React Native 0.87, what does it take to keep an app on JavaScriptCore instead of Hermes, and why do most teams stop doing so?

level: seniorimportance: should knowfreq 30%

basics

~20 s

JavaScriptCore is no longer in core since 0.81: an app must add @react-native-community/javascriptcore and select it in the build. Most teams move to Hermes V1 instead, which core ships, tests and precompiles to bytecode for faster startup.

open as a page

In a React Native app on Hermes, how does the garbage collector keep pauses off the JS thread, and when does React Native trigger a collection itself?

level: seniorimportance: nice to knowfreq 18%

basics

~20 s

Hermes's Hades collector does most old-generation marking and sweeping on a background thread, so the JS thread pauses only briefly, at some throughput cost. React Native also requests a collection on severe Android onTrimMemory levels and, since 0.81, on iOS memory warnings.

open as a page