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

Authors
Davyd Narbutovich
Software Engineer
@
Callstack

If you’re coming from a web development background, you may be familiar with the common wisdom that “the less JavaScript, the better”, especially for performance. To reduce the size of JavaScript, web developers typically will use specialized tools called “minifiers”; the most popular one being Terser.

Such tools hook into the bundling pipeline and convert readable JS into a gibberish mashup of single-, double-, and sometimes triple-character tokens, such as a, __p, or b2, while preserving the code’s behavior.

export function add(left, right) {
  const result = left + right;
  return result;
}
add(2, 3);

JavaScript source code. Size: 95 characters.

export function add(d,n){return d+n}add(2,3);

Minified source code. Size: 45 characters.

The result? Code that’s smaller by even up to 10%-50%. The same tools may often perform dead code elimination (DCE), looking for “dead code” branches, such as those for a “development” environment, but only when our code is intended for “production”.

However, React Native is different. You’ll see that the minification is turned off by default, even in production builds.

Why? The answer is: Hermes.

Hermes dead code elimination

Hermes is a JavaScript engine built for mobile, and React Native apps specifically. It’s the default since 2022 and it was a game-changer for mobile app performance, increasing startup time by up to 40% and reducing memory consumption significantly, while only adding ~2MB to the app bundle.

The interesting thing about Hermes is that when it’s properly configured, it will not run JavaScript directly, but bytecode (or Hermes Bytecode to be exact), which is partially responsible for the perf gains. This has some exciting implications, because the sheer size of our JavaScript doesn’t matter as much, as long as it produces a similar bytecode size. As a result, the React Native team decided it was not worth running extra optimizations in the bundling pipeline, so they removed it.


// https://github.com/facebook/react-native/blob/670a4d942f5eb5c6d406effcd84360958305056b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/TaskConfiguration.kt#L84
// react-native/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/TaskConfiguration.kt
task.minifyEnabled.set(!isHermesEnabledInThisVariant)

// https://github.com/facebook/react-native/blob/670a4d942f5eb5c6d406effcd84360958305056b/packages/react-native/scripts/react-native-xcode.sh#L140-L143
// react-native/packages/react-native/scripts/react-native-xcode.sh
if [[ $USE_HERMES != false && $DEV == false ]]; then
  EXTRA_ARGS+=("--minify" "false")
fi

‍Minification of the source JavaScript is skipped because the Hermes compiler natively performs all necessary optimizations. During the build stage, the compiler applies dead code elimination techniques when generating the final bytecode, which makes a separate minification step redundant and significantly speeds up the build process.

For example, you can see the comparison of Metro bundle sizes using the Expensify app as a real-world test case here.

The observed difference is minimal: 21.79 MB vs 21.62 MB.

Verifying the dead code elimination

How can you verify that your dead code was actually eliminated by the Hermes compiler? You want proof that those unused functions or branches aren't sneaking into your production bundle.

The solution is to use a decompiler to transform the Hermes bytecode (.jsbundle, .bundle files) back into readable JavaScript (.js file). This output allows for direct inspection of the final, processed code, confirming successful dead code elimination by the Hermes compiler.

Quick guide: Decompiling hermes bytecode with hermes-dec

The following steps will guide you through installing and running the powerful hermes-dec tool to inspect your compiled bundles.

Step 1: Install pipx (If necessary)

pipx is a tool to install and run Python applications in isolated environments, making it ideal for the hermes-dec utility. If you don't have it, use Homebrew to install it:

brew install pipx

‍

Step 2: Install the hermes-dec decompiler

Use pipx to install the decompiler directly from its Git repository:

pipx install git+https://github.com/P1sec/hermes-dec

Step 3: Run the decompiler on your bundle

Execute the decompiler, pointing it to your main Hermes bundle (typically ending in .jsbundle for iOS or .bundle for Android) and specifying an output file.

~/.local/pipx/venvs/hermes-dec/bin/hbc-decompiler path/to/main.jsbundle result.js

Once the process is complete, you can open result.js and manually inspect the JavaScript to confirm that the code you expected to be eliminated is, indeed, gone!

This quick check can save you hours of debugging and gives you concrete proof of Hermes's dead code elimination capabilities, ensuring your React Native bundles are as lean as possible.

Conclusion

It’s easy to take some optimizations for granted. And in React Native apps, we have a few layers of tools that perform their own optimizations. We believe it’s worthwhile to understand the whole pipeline, and only do the work where necessary. After all, there are differences between React on the web and React Native, with Hermes as a JavaScript engine being one of the bigger ones. Enjoy hacking around it!

Insights

Learn more about Hermes

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

Dec 4Article

How to Optimize Your Android App Startup Time With Hermes

Learn how to achieve better performance and optimize Android app startup time with Hermes.

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.