A minimal blank Expo app is under 4MB on the App Store. The giant local artifact is a universal dev build — not the shipped product.
Why the Local Artifact is So Large
1. Four CPU architectures bundled into one file
Android supports arm64-v8a, armeabi-v7a, x86, x86_64. Your APK includes compiled native libraries for all four. A user's phone only needs one. So you're shipping 4 apps in one file.
2. The JavaScript engine ships inside your app
React Native bundles Hermes on Android as a compiled .so file (~10MB). On iOS, JSC is built into the OS so it's smaller, but Hermes still adds ~2MB if you enable it.
3. Expo SDK native layer
Managed Expo links Camera, GPS, Sensors, Haptics, Notifications natively — even if your app never uses them. The JS side is tree-shaken since SDK 54, but the native compiled code is always there.
Who Does the Slimming?
Play Store and App Store do it — you don't.
This is called App Thinning (iOS) and App Bundles (Android). You upload the fat universal binary. The store looks at the user's device, generates a custom version with only what that device needs (right chip, right screen density, right language), and delivers that.
It's not compression. It's surgical removal of everything irrelevant to that specific phone.
This only works if you upload AAB (not APK) on Android.
What You Can Optimize From Your End
1. Switch APK → AAB (biggest single impact)
Upload .aab to Play Store, not .apk. This is what enables per-device slimming. Instant 30-40% reduction in user download size with zero code changes.
In eas.json:
JSON
2. Target Only arm64-v8a (if minSdkVersion ≥ 29)
95%+ of active Android devices are 64-bit. x86 is emulators. armeabi-v7a is pre-2014 devices.
Gradle
Halves your native library footprint.
3. Enable Minification + Resource Shrinking
Gradle
4. Filter Locales
Without this, your app bundles string resources for every language in every dependency (Google Play Services alone has dozens).
Gradle
5. Fix JavaScript Imports
Metro bundles everything transitively imported — even code you never call.
JavaScript
Tree shaking handles ESM-native libraries automatically (SDK 54+), but many libs still need explicit cherry-picking.
6. Strip Console Logs in Production
Bash
JavaScript
Every console.log you wrote is shipped to users otherwise.
7. Assets
Convert PNG/JPEG → WebP (25-35% smaller, same quality)
Never bundle videos or large audio — load from URL/CDN at runtime
Only include font weights you actually render
Use vector icons (@expo/vector-icons) instead of multiple PNG sizes
Analyze Your JS Bundle First
Don't optimize blind. Run Atlas and see what's actually large before touching anything.
Bash
Opens an interactive visual map of your bundle — every module, its size, its dependencies. Usually reveals one or two libraries eating 20-30% of the bundle that you forgot about or never needed.