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.
The first release to a deployment is always a full bundle. Every release after that will be a diff, provided the previous release is still available.
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-svgassets. Run as a build step:svgo --folder ./src/assets/icons. - Remove
@1xassets - modern Android and iOS devices no longer use@1ximages. Removing them reportedly reduces bundle size by ~12%. - Avoid
data:imageURIs - 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
- Sentry Integration - upload sourcemaps for symbolicated crash traces
- Code Signing - sign releases for verified delivery
- Troubleshooting - diagnose slow or failing updates
