DEV Community

Ajmal Hasan
Ajmal Hasan

Posted on • Edited on

How I Reduced My React Native 0.81 APK Size by 50% (Without Breaking Anything)

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
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

> 💡 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
}
Enter fullscreen mode Exit fullscreen mode

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"
    }
}
Enter fullscreen mode Exit fullscreen mode

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

# ========== 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:
Enter fullscreen mode Exit fullscreen mode
  1. -optimizationpasses 3 — Multiple optimization passes for maximum shrinkage
  2. Log removal — Strips debug/verbose/info/warning logs (keeps errors for debugging)
  3. -keepattributes SourceFile,LineNumberTable — Preserves stack traces for crash reporting
  4. Comprehensive -keep rules — Protects all React Native internals and common libraries

✅ Step 5: Verify Hermes is Enabled

Hermes is React Native's optimised JavaScript engine. It should be enabled by default in RN 0.81, but verify in your android/gradle.properties:

hermesEnabled=true
Hermes provides:

  • ✅ Faster app startup
  • ✅ Smaller JS bundle size
  • ✅ Lower memory usage

🧪 Testing Your Build

After applying all optimisations:

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-v8a architecture (if minSdk 29+)
  • [ ] Set universalApk false
  • [ ] Add resConfigs for 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

  1. Always test on real devices after applying these optimizations
  2. Keep error logs — don't strip Log.e() for production debugging
  3. Firebase Crashlytics still works — we preserved SourceFile,LineNumberTable
  4. 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 (1)

Collapse
 
dainyjose profile image
Dainy Jose

Clean and practical — this write-up nails it by showing how you cut down a React Native APK from ~85 MB to ~42 MB, proving that you don’t have to sacrifice functionality to ship lighter, faster apps.