DeployPulse

Optimize Assets

OTA update size directly affects download success rates on poor networks and the time users spend waiting for an update to apply. This page covers both built-in DeployPulse optimizations and app-side best practices for keeping bundle sizes small.


Built-in: Differential Packages

DeployPulse automatically generates a differential package for every release after the first. Only files that changed since the previous release are included in the download. The unchanged parts of the bundle are reused from the device's cached copy.

The diff badge in the dashboard's release history confirms that a differential package was generated. No configuration is needed.


Use Hermes

Hermes is the default JavaScript engine in React Native 0.70 and later. If your project was created with RN 0.70+, Hermes is already enabled - no changes needed.

For projects on older React Native versions, enable Hermes in your native config:

android/app/build.gradle

project.ext.react = [
    enableHermes: true
]

ios/Podfile

use_react_native!(
  :hermes_enabled => true
)

dpctl release-react compiles the bundle to Hermes bytecode before uploading it whenever Hermes is enabled for the platform you are releasing, so the device does not have to compile JavaScript at startup. On Android this is detected from hermesEnabled=true in android/gradle.properties. On iOS it is detected from an explicit :hermes_enabled => true in your Podfile, or from the Podfile expo prebuild generates. The React Native 0.70 and later template enables Hermes without writing either, so for those apps pass --useHermes when releasing for iOS.


Exclude Source Maps

Source maps can double the bundle size if accidentally included in the OTA release zip. Always output them to a path outside --outputDir:

dpctl release-react MyApp-iOS ios \
  --outputDir ./release \
  --sourcemapOutput ./sourcemaps/main.jsbundle.map \
  --dev false

If you use Sentry, upload the map file separately after releasing. See the Sentry Integration guide.


Image Optimization

Images frequently account for 60–80% of bundle size. A few changes can have a large impact:

  • react-native-imagemin-asset-plugin - Metro plugin that automatically compresses PNG, JPG, and WebP assets at build time with no quality loss.
  • SVGO - lossless SVG optimization for react-native-svg assets. Run as a build step: svgo --folder ./src/assets/icons.
  • Remove @1x assets - modern Android and iOS devices no longer use @1x images. Removing them reportedly reduces bundle size by ~12%.
  • Avoid data:image URIs - inline base64 images inflate the JS bundle and prevent differential updates from being effective. Always reference assets by path.

Reduce JS Bundle Size

  • react-native-bundle-visualizer - runs Metro and opens an interactive treemap to identify which dependencies are largest.
  • Knip - static analysis tool that detects unused exports and files. Removing dead code shrinks the bundle and improves diff efficiency between releases.
  • Avoid large JSON files in the bundle - localization files and feature flag payloads are common offenders. Lazy-load them at runtime or fetch from a remote endpoint instead of bundling.

Font Optimization

Fontmin generates subset fonts that contain only the Unicode characters your app actually uses. Subsetting a custom font from 400KB to 40KB is common for apps that use a limited character set.


Release Flags

Always build releases with:

dpctl release-react MyApp-iOS ios \
  --deploymentName Production \
  --dev false \
  --minify true
  • --dev false - disables the React Native DevTools bridge, assertions, and verbose logging. Required for production.
  • --minify true - applies additional Terser minification on top of Metro's default output.

Further Reading