Mobile Development 13 min read

Optimizing Hermes Bytecode Bundle Size and SourceMap Handling in React Native

To counter the 40‑100 % size increase of Hermes bytecode bundles after upgrading to React Native 0.70, the team switched to xz compression (cutting zip size 20‑26 %), enabled hermesc ‑O optimization (shrinking bundles 10‑22 % while requiring a two‑step source‑map merge), and used the ‑base‑bytecode option with bsdiff to reduce incremental OTA patches by up to 85 %, collectively improving download and update efficiency.

NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
Optimizing Hermes Bytecode Bundle Size and SourceMap Handling in React Native

Background : After upgrading React Native to 0.70 the Hermes engine is used. Hermes provides pre‑compilation and bytecode execution, but converting a JS bundle to a Hermes Bytecode Bundle (HBC) increases the ZIP size by 40%‑100% and the incremental package size by 2‑3×.

To mitigate the size growth, two main approaches were explored:

Changing the compression method of the final product.

Optimizing the export of the bundle itself.

Compression Method Comparison

Historically a simple zip was used. Three higher‑ratio algorithms were evaluated: gzip , bzip2 and xz .

Algorithm Details

gzip : uses the DEFLATE algorithm.

bzip2 : uses Burrows‑Wheeler transform + Huffman coding.

xz : uses the LZMA algorithm.

Performance Metrics (compression level 6)

Compression speed: xz = 1 min 27 s, gzip = 5 s, bzip2 = 8 s.

Memory usage during compression: xz = 97 656 KB, gzip = 2 048 KB, bzip2 = 6 164 KB.

Compression ratio: xz = 73.62 %, bzip2 = 70.32 %, gzip = 63.48 %.

Decompression speed: gzip = 0.8 s, xz = 1 s 9, bzip2 = 5 s 5.

Decompression memory: xz = 10 580 KB, gzip = 1 876 KB, bzip2 = 3 812 KB.

Considering that compression speed and memory are not user‑visible (compression runs on the build server), the highest compression ratio and acceptable decompression speed lead to choosing xz as the new compression method.

Applying xz reduced the ZIP‑compressed HBC bundle size by 20‑26 % across several product variants.

Bundle Export Optimizations

When converting a plain text bundle to an HBC bundle, the Hermes compiler ( hermesc ) offers an -O flag for highest‑level optimization. Using -O yields a 10 %‑22 % reduction in bundle size, mainly by stripping the Symbol Table (SourceMap) from the output.

Example command to generate a plain bundle with a SourceMap:

npx react-native bundle --platform ios --dev false --entry-file index.js --bundle-output ./build/index.ios.bundle --sourcemap-output ./build/index.ios.bundle.packager.map

Generating an optimized HBC bundle:

hermesc -O -emit-binary -output-source-map -out=./build/index.ios.bundle.hbc ./build/index.ios.bundle

Because the -O flag removes the Symbol Table, stack traces from a production HBC bundle contain only line 1 information, making debugging impossible.

Solution: export a separate SourceMap for the HBC bundle using the --sourcemap-output option, then combine it with the original plain‑bundle SourceMap. The combined map allows a two‑step resolution:

Use the HBC bundle SourceMap to map the optimized bytecode location back to the original plain‑bundle line/column.

Use the plain‑bundle SourceMap to resolve that line/column to the original source file and symbol.

Both maps can be merged into a single file with:

./node_modules/react-native/scripts/compose-source-maps.js ./build/index.ios.bundle.packager.map ./build/index.ios.bundle.hbc.map -o ./build/index.ios.bundle.map

Incremental Package Reduction

Incremental updates are generated with bsdiff . The Hermes compiler provides a -base-bytecode option to specify a base bytecode file that contains shared code. When this option is used, only the delta between the new bundle and the base is stored, dramatically shrinking the diff.

Example without -base-bytecode :

// Generate new HBC (≈2.63 MB)
hermes -emit-binary ./test1.bundle -out ./test1.hbc
// Generate patch (≈65 KB)
bsdiff test.hbc test1.hbc patchfile

Example with -base-bytecode (using a pre‑built base file):

// Generate new HBC (size unchanged ≈2.65 MB)
hermes -emit-binary -base-bytecode='baseBytecodeTest.hbc' ./test1.bundle -out ./test1.hbc
// Generate patch (≈9 KB)
bsdiff baseBytecodeTest.hbc test1.hbc patchfile

Using -base-bytecode reduces the incremental package size by 80 %‑85 % compared with the naïve approach.

Conclusions

Applying -O during HBC bundle generation reduces bundle size by 10 %‑22 %.

When -O is used, the HBC bundle’s stack traces must be resolved via a two‑step SourceMap process (HBC → plain bundle → source).

Alternatively, merge the two SourceMaps into a single map for one‑step resolution.

Using -base-bytecode together with bsdiff cuts incremental package size by up to 85 %.

These optimizations improve download size and OTA update efficiency for React Native applications.

BytecodeReact NativeIncremental UpdatecompressionHermesSourcemap
NetEase Cloud Music Tech Team
Written by

NetEase Cloud Music Tech Team

Official account of NetEase Cloud Music Tech Team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.