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.