A large APK size can slow down downloads, increase install drop-offs, and impact user experience — especially in regions with slower internet connections.
In this post, I'll walk you through the exact steps I used to reduce a production React Native 0.81 app's APK size by ~50%, while maintaining full functionality and security.
📊 The Results
| Before | After | Reduction |
|---|---|---|
| ~85 MB | ~42 MB | ~50% |
🛠️ Step 1: Build Only for arm64-v8a Architecture
Most optimization guides tell you to split APKs by CPU architecture. But here's the thing: if your minSdkVersion is 29+ (Android 10+), you only need arm64-v8a.
Why? Because:
- ✅ All Android 10+ devices support 64-bit (arm64-v8a)
- ✅ Google Play Store requires 64-bit support
- ✅ 95%+ of active Android devices use arm64-v8a
- ❌ armeabi-v7a is for 32-bit devices (pre-2014)
- ❌ x86/x86_64 is for Intel devices (<1% market share)
Implementation
In your android/app/build.gradle:
// Enable separate builds per CPU architecture
def enableSeparateBuildPerCPUArchitecture = true
android {
// ... existing config ...
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // No universal APK needed
include "arm64-v8a" // Only modern 64-bit devices
}
}
}💡 Pro tip: Set universalApk false to skip building a fat universal APK — it's unnecessary when targeting a single architecture.
🌍 Step 2: Keep Only Necessary Languages
Your app probably only supports a few languages, but by default, Android bundles resources for ALL languages from your dependencies (like Google Play Services).
Implementation
In android/app/build.gradle, inside defaultConfig:
defaultConfig {
// ... existing config ...
// Keep only necessary languages to reduce APK size
resConfigs "ar", "en", "fr" // Add your supported languages
}This single line can remove several megabytes of unused language resources!
📦 Step 3: Enable Resource Shrinking & PNG Compression
Enable these in your release build type:
buildTypes {
release {
signingConfig signingConfigs.release
debuggable false
minifyEnabled true // Code shrinking (already enabled)
shrinkResources true // Remove unused resources
crunchPngs true // Extra PNG compression
zipAlignEnabled true // Memory optimization
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
}
}### What Each Does:
| Option | Benefit |
|---|---|
minifyEnabled true |
Removes unused code, obfuscates names |
shrinkResources true |
Removes unused drawables, layouts, etc. |
crunchPngs true |
Additional PNG compression |
zipAlignEnabled true |
Optimizes APK for memory-mapped reading |
🔐 Step 4: Optimize ProGuard Rules
ProGuard (or R8) is powerful but can break your app if not configured correctly. Here's a comprehensive, battle-tested configuration for React Native 0.81:
android/app/proguard-rules.pro
guard
========== STANDARD OPTIMIZATIONS ==========
-optimizationpasses 3
-allowaccessmodification
-overloadaggressively
-repackageclasses ''
-dontusemixedcaseclassnames
-dontpreverify
Remove debug logging in production (keeps error logs)
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
public static *** i(...);
public static *** w(...);
}
Keep crash reporting info
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute SourceFile
-keepattributes Signature, Annotation
========== REACT NATIVE CORE ==========
-keep class com.facebook.hermes.** { ; }
-keep class com.facebook.hermes.unicode.* { ; }
-keep class com.facebook.react.* { ; }
-keep class com.facebook.jni.* { ; }
-keep class com.facebook.soloader.* { *; }
Bridge & Native Modules
-keep public class * extends com.facebook.react.bridge.NativeModule { ; }
-keep class com.facebook.react.bridge.* { *; }
-keepclassmembers class * {
@com.facebook.react.bridge.ReactMethod *;
}
-keepclassmembers class * {
@com.facebook.proguard.annotations.DoNotStrip *;
}
UI Manager & View System
-keep @com.facebook.react.uimanager.annotations.ReactProp class * { ; }
-keep @com.facebook.react.uimanager.annotations.ReactPropGroup class * { *; }
-keep class * extends com.facebook.react.uimanager.ViewManager { *; }
-keep class com.facebook.react.uimanager.* { *; }
TurboModules (New Architecture)
-keep class com.facebook.react.turbomodule.** { ; }
-keep class com.facebook.react.animated.* { ; }
-keep class com.facebook.react.common.* { *; }
========== COMMON LIBRARIES ==========
Reanimated
-keep class com.swmansion.reanimated.** { ; }
-keep class com.swmansion.* { ; }
-dontwarn com.swmansion.*
SVG
-keep public class com.horcrux.svg.** { *; }
Fast Image
-keep class com.dylanvann.fastimage.** { *; }
Firebase
-keep class com.google.firebase.** { ; }
-dontwarn com.google.firebase.*
OkHttp
-keep class okhttp3.** { ; }
-keep interface okhttp3.* { ; }
-dontwarn okhttp3.*
-dontwarn okio.**### Why This Works:
-
-optimizationpasses 3— Multiple optimization passes for maximum shrinkage - Log removal — Strips debug/verbose/info/warning logs (keeps errors for debugging)
-
-keepattributes SourceFile,LineNumberTable— Preserves stack traces for crash reporting -
Comprehensive
-keeprules — Protects all React Native internals and common libraries
✅ Step 5: Verify Hermes is Enabled
Hermes is React Native's optimized JavaScript engine. It should be enabled by default in RN 0.81, but verify in your android/gradle.properties:
hermesEnabled=trueHermes provides:
- ✅ Faster app startup
- ✅ Smaller JS bundle size
- ✅ Lower memory usage
🧪 Testing Your Build
After applying all optimizations:
Clean previous builds
cd android && ./gradlew clean
Build release APK
./gradlew assembleRelease
Check APK size
ls -lh app/build/outputs/apk//release/.apk---
📋 Complete Checklist
- [ ] Set
enableSeparateBuildPerCPUArchitecture = true - [ ] Include only
arm64-v8aarchitecture (if minSdk 29+) - [ ] Set
universalApk false - [ ] Add
resConfigsfor your supported languages - [ ] Enable
shrinkResources true - [ ] Enable
crunchPngs true - [ ] Enable
zipAlignEnabled true - [ ] Add comprehensive ProGuard rules
- [ ] Verify Hermes is enabled
- [ ] Test on real devices before release!
🎯 Summary
| Optimization | APK Size Impact |
|---|---|
| Single architecture (arm64-v8a) | -30-40% |
| Resource shrinking | -5-10% |
| Language filtering | -2-5% |
| ProGuard optimizations | -5-10% |
| PNG compression | -1-3% |
⚠️ Important Notes
- Always test on real devices after applying these optimizations
-
Keep error logs — don't strip
Log.e()for production debugging -
Firebase Crashlytics still works — we preserved
SourceFile,LineNumberTable - Play Store accepts single-architecture APKs — they'll deliver the right one to users
Hope this helps you ship a lighter, faster React Native app! 🚀
Got questions or improvements? Drop them in the comments! 👇
Found this useful? Follow me for more React Native optimization tips!
Top comments (0)