Reducing React Native App Size: Bundle and Asset Optimization
AI generated
RN
native
React Native · App Size · Build Tooling · Assets
Reducing React Native app size
optimizing bundle and assets systematically

The app size of a React Native app determines install rates, store warnings, and reach in markets with slow connections. Android App Bundle with R8 shrinking, iOS App Thinning, WebP images, and continuous size monitoring in the CI pipeline shrink app size measurably, without sacrificing functionality.

14 min read AAB · ProGuard/R8 · App Thinning · WebP Android · iOS · Fastlane · CI/CD

1. Why app size is a business metric

A large app size is not merely a technical detail, it is a measurable figure with direct impact on install rates. On slow or metered connections, users abandon the download before installation even begins, especially when the Play Store or App Store already shows a size warning before a user even taps "Install". In growth markets with limited mobile data plans, every extra megabyte costs real, measurable installs.

The Play Store shows explicit download size warnings past certain thresholds, and the App Store restricts downloading larger apps over cellular without a Wi-Fi confirmation. Both mechanisms directly affect the conversion rate between a store page visit and an actual install, an effect that shows up clearly in app analytics.

Before working on app size, it helps to set a concrete target: a typical production React Native app bundle should stay well under 50 MB on Android and under 100 MB on iOS, depending on feature scope and target audience. That target is the baseline against which every optimization step below gets measured.

2. Where the bytes actually go

Before reducing app size, you need to know where the megabytes actually go. A typical React Native app size budget splits across the JavaScript bundle, native binaries or architecture slices, images and fonts, and native third-party SDKs. Without this breakdown, optimization turns into guesswork, spending time in the wrong place.

The Android APK Analyzer, built directly into Android Studio, visualizes the size distribution of an APK or AAB by file type and directory, immediately showing whether native libraries, resources, or the JS bundle account for the largest share. On iOS, Xcode provides an app size report (App Thinning Size Report) that breaks down size per device variant and shows how much each architecture slice contributes to the total package.

These analysis steps should sit at the start of every optimization round, not after. Measuring first tells you whether the effort of configuring ProGuard, converting to WebP, or removing a native SDK is even worth it before spending development time.

3. Android: App Bundle, R8, and ABI splits

The single most effective lever for reducing app size on Android is switching from a classic APK to an Android App Bundle (.aab). Play Feature Delivery generates an optimized, device-specific APK for each device from it, containing only the resources and CPU architectures actually needed, instead of shipping a universal APK with everything for everyone.

Additionally, R8, the successor to ProGuard, reduces size through code shrinking (removing unused code), resource shrinking, and obfuscation. Together, these three steps can noticeably cut the final app footprint, especially in projects with many unused library functions or resources coming from third-party SDKs.


// android/app/build.gradle
android {
    buildTypes {
        release {
            // Enable R8 code shrinking and obfuscation
            minifyEnabled true
            // Remove unused resources not referenced by shrunk code
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    // Split output by CPU architecture instead of shipping all ABIs
    splits {
        abi {
            enable true
            reset()
            include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
            universalApk false
        }
    }
    bundle {
        // Let Play Feature Delivery generate per-device optimized APKs
        language { enableSplit = true }
        density { enableSplit = true }
        abi { enableSplit = true }
    }
}

A commonly overlooked side effect of ABI splits: without this configuration, the same APK containing all four CPU architectures is shipped for every install, even though a device only needs a single one. This app size reduction is therefore a pure configuration win, without changing a single line of app code.

4. iOS: App Thinning and architecture slices

On iOS, App Thinning plays a similar role to AAB splits on Android: the App Store delivers only the matching assets and architecture slices for each device, instead of distributing a universal package with every image variant and CPU architecture. On-Demand Resources complements this by loading assets not needed immediately at launch only when actually required, instead of bundling them permanently into the initial download.

A common but easily fixable problem in React Native apps with many third-party frameworks: these frameworks often also contain simulator slices or architectures not needed at all for the final store build. Removing these unused slices from embedded frameworks before archiving noticeably reduces app size, without affecting any functionality on real devices.

Asset catalogs (`.xcassets`) with correctly configured compression are the second lever: images that accidentally end up uncompressed or at too high a resolution in the catalog needlessly inflate app size. Xcode compresses asset catalogs automatically, but only if the source images themselves are not already inefficiently large raw data.

5. Analyzing JS bundle size

Compared to modern web bundlers, React Native's Metro bundler has only limited tree-shaking, meaning accidentally imported dead code often ends up in the final bundle instead of being automatically removed. This gap makes targeted bundle analysis an important step in reducing app size.

Tools like `react-native-bundle-visualizer` or `source-map-explorer` visualize which modules take up how much space in the JS bundle, usually as an interactive treemap. A classic finding: a full import of `lodash` instead of a single function, or an entire icon font set, even though the app only actually uses a handful of icons.


# Generate a source map and analyze bundle composition
npx react-native bundle \
  --platform android \
  --dev false \
  --entry-file index.js \
  --bundle-output android-release.bundle \
  --sourcemap-output android-release.bundle.map

npx source-map-explorer android-release.bundle android-release.bundle.map

The most common finding from this analysis is not a single giant module, but many small, accidentally fully imported libraries whose sum contributes significantly to app size. Named imports instead of default imports of entire libraries usually solve this problem with no functional loss.

6. Optimizing images and assets

Images are the single largest contributor to app size in most React Native apps, often bigger than the entire JS bundle. Converting PNG to WebP often reduces file size substantially with barely perceptible quality loss, without needing to adjust layout or rendering code.

A second common mistake: instead of correct `@2x`/`@3x` asset variants, a single oversized image gets used for all screen densities. The operating system then scales this image down at runtime, while the app carries around unnecessarily large raw data for every display density. Correctly sized assets per density level are usually the fastest way to save noticeable space.


# Convert PNG assets to WebP with high quality, low size overhead
for f in assets/images/*.png; do
  cwebp -q 85 "$f" -o "${f%.png}.webp"
done

# Compare original vs converted size
du -sh assets/images/*.png assets/images/*.webp

SVG is often the most space-efficient alternative to raster images for simple icons and illustrations, since a single vector format covers all resolutions instead of maintaining several raster versions. For photos and complex images, WebP remains the better choice. Remote images not needed in every case should generally be lazy-loaded rather than baked permanently into the app bundle.

7. Auditing native dependencies for bloat

Native SDKs for analytics, ads, or payment processing often add several megabytes per library, independent of the app's actual JS code. These costs show up directly in app size and are easily overlooked when looking only at the JS bundle, because they occur exclusively at the native level.

After every major feature release, it is worth taking stock of the autolinking configuration: native modules no longer referenced by removed JS code often stay linked in the build configuration anyway and keep getting compiled in. An audit of actually used native packages against `package.json` reliably uncovers such leftovers.

Overlapping libraries are another common pattern: two different native HTTP client modules, an old one and a newly introduced one existing side by side in the project, double native dependency costs with no additional benefit. Consolidating onto a single library per functional area often reduces app size surprisingly noticeably.

8. Continuous size monitoring in CI

Without continuous monitoring, app size creeps up gradually with every feature, every new dependency, and every added image, often with no single commit that can be blamed for it. A size budget in the CI pipeline makes this gradual increase visible before it accumulates across many releases.

A simple approach: after every build, measure the resulting AAB or IPA size and compare it against a defined threshold. If the build exceeds that value, the pipeline fails or flags the pull request for manual review, similar to a performance regression test.


#!/usr/bin/env bash
# ci-check-app-size.sh: fail the build if AAB size regresses
set -euo pipefail

MAX_SIZE_MB=45
AAB_PATH="android/app/build/outputs/bundle/release/app-release.aab"

size_bytes=$(stat -c%s "$AAB_PATH")
size_mb=$((size_bytes / 1024 / 1024))

echo "Current AAB size: ${size_mb} MB (budget: ${MAX_SIZE_MB} MB)"

if (( size_mb > MAX_SIZE_MB )); then
  echo "ERROR: App size exceeds budget of ${MAX_SIZE_MB} MB" >&2
  exit 1
fi

Fastlane lanes can be extended with such size checks and additionally provide a historical record documenting how app size evolved release by release. This discipline treats size regressions with the same care as performance regressions, instead of only noticing them once users or store reviews already react.

9. App size reduction techniques compared

Not every technique delivers the same return relative to effort. The table below places the most important options for reducing app size side by side.

Technique Typical savings Implementation effort Risk
R8/ProGuard shrinking High Low, one-time config Low, secure with testing
Android App Bundle / ABI splits High Low Very low
WebP conversion Medium Low, batch script Low, visual check needed
Removing unused dependencies Variable, sometimes high Medium, audit needed Medium, functionality risk

R8 shrinking and AAB splits deliver the best ratio of savings to effort, since they are almost purely configuration changes. Removing unused dependencies requires more care, since an incomplete audit can break functionality, but it often shrinks app size the most when done correctly.

Mironsoft

App size audits and build optimization for React Native

Want to measurably shrink app size without risking functionality?

We analyze your AAB, IPA and JS bundle, identify the biggest savings potential, and set up R8 shrinking, AAB splits, and CI size monitoring, so your app size stays within target permanently.

Size audit

APK Analyzer and App Thinning report analysis with concrete savings potential

Build configuration

Setting up R8 shrinking, AAB splits, and App Thinning correctly and safely

CI size budget

Automated size checks per build, before regressions go live

10. Summary

The app size of a React Native app is most effectively reduced with a combination of build configuration and asset discipline: Android App Bundle with ABI splits and R8 shrinking on Android, App Thinning and cleaned up framework slices on iOS, WebP conversion for images, and targeted JS bundle analysis against accidentally fully imported libraries.

The most sustainable lever, however, is continuous monitoring rather than a one-time optimization pass: a size budget in the CI pipeline prevents gradual growth from accumulating unnoticed across many releases. Treating app size as a recurring metric instead of a one-off project is what keeps it within target permanently.

Reducing React Native App Size: Key Takeaways

Android App Bundle

AAB with ABI splits delivers device-specific, smaller APKs instead of a universal build.

R8 shrinking

`minifyEnabled true` and `shrinkResources true` automatically strip unused code and resources.

Images & assets

WebP conversion and correct density variants often save the single largest share.

CI size budget

Automated size checks per build prevent gradual, unnoticed growth.

11. FAQ: Reducing React Native App Size

1Why does app size matter?
It lowers install rates on slow connections and in markets with limited data. Store size warnings further reduce conversion.
2What does Android App Bundle achieve?
Play Feature Delivery ships device-specific APKs with only needed resources and architectures instead of a universal APK.
3What does R8 do exactly?
Removes unused code and resources and obfuscates the rest. Enabled via minifyEnabled true and shrinkResources true.
4How do I find out where size comes from?
Android APK Analyzer and Xcode's App Thinning size report break size down by file type and device variant.
5Why WebP instead of PNG?
WebP reduces file size substantially with barely perceptible quality loss, automatable with cwebp.
6What is App Thinning?
Delivers only matching assets and architecture slices per device. On-Demand Resources loads rarely needed assets on demand.
7How do I find fully imported libraries?
react-native-bundle-visualizer or source-map-explorer visualize bundle composition as a treemap.
8How do I audit native dependencies?
An audit against package.json reveals unused or overlapping native modules that can be consolidated.
9How do I monitor app size continuously?
A CI script measures build size and fails when a threshold is exceeded, like a performance regression test.
10Which technique saves the most for the least effort?
R8 shrinking and AAB with ABI splits, since both are almost purely build configuration.