React Native Performance Optimisation for APAC Low-Bandwidth Networks


Key Takeaways
- Profile on real APAC network conditions, not office Wi-Fi
- Network-aware hooks adapt image quality and page sizes automatically
- FlashList cuts scroll frame drops by up to 90% on budget devices
- ABI splitting and Hermes reduce APK and bundle size by 30-46%
- Offline-first persistence is essential for intermittent APAC connectivity
Quick Answer: Optimise React Native for APAC low-bandwidth by profiling under realistic network throttles, enabling Hermes, implementing network-aware data fetching hooks, using adaptive image loading, migrating to FlashList, adding offline-first persistence with WatermelonDB, and reducing APK size via ABI splitting.
Most React Native performance guides assume your users sit on stable 4G or Wi-Fi in San Francisco. That assumption breaks immediately across APAC, where according to Opensignal's 2024 Mobile Network Experience Report, average download speeds in the Philippines hover around 13 Mbps, Indonesia at 16 Mbps, and rural Vietnam can dip below 5 Mbps on congested 3G towers. React Native performance optimisation for APAC low-bandwidth conditions demands a fundamentally different playbook — one tuned for packet loss, high latency, and the budget Android devices that dominate Southeast Asian markets.
Related reading: Claude AI FreeBSD Kernel Vulnerability Exploit: What It Means for APAC Security Teams
Related reading: Cross-Border Returns Management for APAC E-Commerce Brands: A 7-Step Integration Guide
Related reading: Top Customer Data Platforms for APAC Retail 2026: A Buyer's Scoring Guide
Related reading: Composable Commerce vs Monolithic Platform TCO Analysis: A 3-Year APAC Model
Related reading: Data Governance Framework for APAC Retail Multi-Market Ops: A 7-Step Guide
I learned this firsthand at Lazada, where we shipped mobile experiences to millions of users across six countries on networks that would make most Western QA teams panic. At Branch8, our engineering teams in Taiwan, Vietnam, and the Philippines now build and test React Native apps under these real-world constraints daily. This tutorial distils that experience into concrete, copy-pasteable steps.
Prerequisites
Before starting, ensure you have the following:
- React Native 0.73+ (New Architecture support recommended; Hermes enabled by default)
- Node.js 18 LTS or later
- Android Studio Hedgehog (2023.1.1) with a configured emulator or physical device
- Xcode 15+ for iOS builds
- Flipper 0.230+ for profiling (or React Native DevTools if you've migrated)
- A network throttling tool — we use Charles Proxy 4.6 or Android's built-in Developer Options network throttling
- Familiarity with
metro.config.js,babel.config.js, and basic React Native navigation
Optional but strongly recommended: a physical budget Android device. We keep a Samsung Galaxy A04 (2GB RAM, ~USD 100) and a Realme C30 in every Branch8 office specifically for low-end testing — these represent actual devices carried by millions of APAC users.
Step 1: Profile Under Realistic APAC Network Conditions
Before optimising anything, you need accurate baselines. The biggest mistake teams make is profiling on office Wi-Fi and calling it done.
Configure Charles Proxy for APAC Throttle Profiles
Create custom throttle profiles that mirror real APAC conditions. Based on Ookla Speedtest Intelligence Q1 2024 data, here are the profiles we use at Branch8:
1{2 "profiles": [3 {4 "name": "PH_Rural_3G",5 "bandwidth_down_kbps": 2500,6 "bandwidth_up_kbps": 800,7 "latency_ms": 280,8 "packet_loss_pct": 39 },10 {11 "name": "VN_Urban_4G",12 "bandwidth_down_kbps": 12000,13 "bandwidth_up_kbps": 4000,14 "latency_ms": 85,15 "packet_loss_pct": 0.516 },17 {18 "name": "ID_Suburban_Mixed",19 "bandwidth_down_kbps": 5000,20 "bandwidth_up_kbps": 1500,21 "latency_ms": 180,22 "packet_loss_pct": 223 },24 {25 "name": "TW_Urban_5G",26 "bandwidth_down_kbps": 85000,27 "bandwidth_up_kbps": 15000,28 "latency_ms": 25,29 "packet_loss_pct": 0.130 }31 ]32}
Save this as apac-throttle-profiles.json and import it into Charles Proxy via Proxy → Throttle Settings → Import.
Android Device-Level Throttling
If you're testing on a physical Android device without Charles:
1# Connect device via ADB2adb shell settings put global captive_portal_mode 034# Simulate 3G-equivalent throttle (requires root or use Android emulator)5# In Android Emulator, use Extended Controls > Cellular > Network Type: EDGE/3G6# Set signal strength: Moderate
Run your app and record these baseline metrics with Flipper's performance plugin:
- Time to Interactive (TTI) from cold start
- JS thread frame drops during scroll
- Network waterfall — total payload size and request count on initial load
- Memory usage at peak and idle
Document these numbers. You'll compare after each optimisation step.
Ready to Transform Your Ecommerce Operations?
Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.
Step 2: Slash Your JavaScript Bundle Size
Bundle size directly impacts startup time, especially on low-bandwidth networks. According to Callstack's 2024 State of React Native report, the median production bundle sits around 6-8 MB — that's a 25-second download on a Philippine rural 3G connection.
Enable Hermes and Verify It's Actually Running
Hermes should be default on RN 0.73+, but we've encountered projects where it was accidentally disabled. Verify:
1// Add this to your App.tsx to verify at runtime2import { Platform } from 'react-native';34console.log(5 'Hermes enabled:',6 typeof HermesInternal !== 'undefined' ? 'YES' : 'NO'7);
If Hermes is not active, enable it explicitly:
1// android/app/build.gradle2project.ext.react = [3 enableHermes: true,4 hermesCommand: "../../node_modules/react-native/sdks/hermesc/%OS-BIN%/hermesc"5]
Analyse and Tree-Shake Your Bundle
Install react-native-bundle-visualizer to identify bloat:
1npx react-native-bundle-visualizer
This generates a treemap of your bundle. In a recent Branch8 project — a fintech app targeting Vietnam and Philippines markets — we discovered that moment.js with all locales accounted for 320KB of the bundle. Replacing it with date-fns (importing only vi and fil locales) cut that to 18KB:
1// Before: 320KB2import moment from 'moment';3import 'moment/locale/vi';45// After: 18KB6import { format, parseISO } from 'date-fns';7import { vi } from 'date-fns/locale';89const formatted = format(parseISO(dateString), 'dd MMM yyyy', { locale: vi });
Configure Metro for Aggressive Tree Shaking
1// metro.config.js2const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');34const config = {5 transformer: {6 minifierConfig: {7 compress: {8 drop_console: true, // Remove console.log in production9 drop_debugger: true,10 passes: 3, // Multiple compression passes11 pure_getters: true,12 },13 mangle: {14 toplevel: true,15 },16 },17 },18};1920module.exports = mergeConfig(getDefaultConfig(__dirname), config);
Expected result: 15-30% bundle size reduction depending on your dependency graph.
Step 3: Implement Intelligent Network-Aware Data Fetching
This is where APAC-specific React Native performance optimisation under low-bandwidth conditions diverges most sharply from Western guides. You can't just "add caching" — you need network-aware strategies that adapt in real time.
Detect Network Quality and Adapt
1npm install @react-native-community/netinfo
1// hooks/useNetworkAwareConfig.ts2import NetInfo, { NetInfoStateType } from '@react-native-community/netinfo';3import { useEffect, useState } from 'react';45interface NetworkConfig {6 imageQuality: 'low' | 'medium' | 'high';7 pageSize: number;8 prefetchEnabled: boolean;9 timeoutMs: number;10 retryCount: number;11}1213const NETWORK_CONFIGS: Record<string, NetworkConfig> = {14 poor: {15 imageQuality: 'low',16 pageSize: 5,17 prefetchEnabled: false,18 timeoutMs: 30000,19 retryCount: 5,20 },21 moderate: {22 imageQuality: 'medium',23 pageSize: 15,24 prefetchEnabled: true,25 timeoutMs: 15000,26 retryCount: 3,27 },28 good: {29 imageQuality: 'high',30 pageSize: 25,31 prefetchEnabled: true,32 timeoutMs: 8000,33 retryCount: 2,34 },35};3637export function useNetworkAwareConfig(): NetworkConfig {38 const [config, setConfig] = useState<NetworkConfig>(NETWORK_CONFIGS.moderate);3940 useEffect(() => {41 const unsubscribe = NetInfo.addEventListener((state) => {42 if (!state.isConnected) {43 setConfig(NETWORK_CONFIGS.poor);44 return;45 }4647 if (state.type === NetInfoStateType.cellular) {48 const generation = state.details?.cellularGeneration;49 if (generation === '2g' || generation === '3g') {50 setConfig(NETWORK_CONFIGS.poor);51 } else {52 setConfig(NETWORK_CONFIGS.moderate);53 }54 } else if (state.type === NetInfoStateType.wifi) {55 setConfig(NETWORK_CONFIGS.good);56 }57 });5859 return () => unsubscribe();60 }, []);6162 return config;63}
Wire It Into Your API Layer
1// api/client.ts2import { useNetworkAwareConfig } from '../hooks/useNetworkAwareConfig';34export function createFetchWithRetry(config: ReturnType<typeof useNetworkAwareConfig>) {5 return async function fetchWithRetry(url: string, options: RequestInit = {}) {6 const controller = new AbortController();7 const timeout = setTimeout(() => controller.abort(), config.timeoutMs);89 for (let attempt = 0; attempt <= config.retryCount; attempt++) {10 try {11 const response = await fetch(url, {12 ...options,13 signal: controller.signal,14 headers: {15 ...options.headers,16 // Tell your API to send compressed/reduced payloads17 'X-Network-Quality': config.imageQuality,18 'Accept-Encoding': 'gzip, br',19 },20 });21 clearTimeout(timeout);22 return response;23 } catch (err) {24 if (attempt === config.retryCount) throw err;25 // Exponential backoff with jitter26 await new Promise((r) =>27 setTimeout(r, Math.min(1000 * Math.pow(2, attempt) + Math.random() * 500, 10000))28 );29 }30 }31 };32}
Ready to Transform Your Ecommerce Operations?
Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.
Step 4: Optimise Images for Variable Bandwidth
Images account for 60-80% of mobile data transfer according to HTTP Archive's 2024 Web Almanac. In low-bandwidth APAC markets, unoptimised images are the single biggest performance killer.
Use react-native-fast-image with Quality Tiers
1npm install react-native-fast-image
1// components/AdaptiveImage.tsx2import FastImage from 'react-native-fast-image';3import { useNetworkAwareConfig } from '../hooks/useNetworkAwareConfig';45interface AdaptiveImageProps {6 baseUrl: string;7 width: number;8 height: number;9}1011const QUALITY_SUFFIXES = {12 low: '?w=320&q=40&fm=webp',13 medium: '?w=640&q=65&fm=webp',14 high: '?w=1080&q=80&fm=webp',15};1617export function AdaptiveImage({ baseUrl, width, height }: AdaptiveImageProps) {18 const { imageQuality } = useNetworkAwareConfig();1920 return (21 <FastImage22 style={{ width, height }}23 source={{24 uri: `${baseUrl}${QUALITY_SUFFIXES[imageQuality]}`,25 priority:26 imageQuality === 'low'27 ? FastImage.priority.low28 : FastImage.priority.normal,29 cache: FastImage.cacheControl.immutable,30 }}31 resizeMode={FastImage.resizeMode.cover}32 />33 );34}
This assumes your image CDN (Cloudinary, Imgix, or AWS CloudFront with Lambda@Edge) supports URL-based transformations. If you're serving from a basic S3 bucket — stop. Set up CloudFront with an image transformation layer first.
Precompute Placeholder Hashes
Generate BlurHash strings server-side so users see immediate low-resolution placeholders:
1// Use react-native-blurhash for instant visual feedback2import { Blurhash } from 'react-native-blurhash';34// In your image component, show blurhash while FastImage loads5{isLoading && (6 <Blurhash7 blurhash={item.blurhash} // e.g. "LGF5]+Yk^6#M@-5c,1J5@[or[Q6."8 style={{ width, height, position: 'absolute' }}9 />10)}
Step 5: Implement Offline-First Data Persistence
In many APAC markets, network connectivity is intermittent — think users on Manila's MRT, Ho Chi Minh City's metro, or rural Indonesian regions. Your app must function without a network.
Set Up WatermelonDB for Local-First Architecture
1npm install @nozbe/watermelondb @nozbe/with-observables2npx pod-install # iOS only
1// database/schema.ts2import { appSchema, tableSchema } from '@nozbe/watermelondb';34export const schema = appSchema({5 version: 1,6 tables: [7 tableSchema({8 name: 'products',9 columns: [10 { name: 'remote_id', type: 'string', isIndexed: true },11 { name: 'title', type: 'string' },12 { name: 'price', type: 'number' },13 { name: 'thumbnail_url', type: 'string' },14 { name: 'synced_at', type: 'number' },15 { name: 'is_dirty', type: 'boolean' }, // Track local changes16 ],17 }),18 ],19});
1// database/sync.ts2import { synchronize } from '@nozbe/watermelondb/sync';3import { database } from './index';45export async function syncWithServer() {6 await synchronize({7 database,8 pullChanges: async ({ lastPulledAt }) => {9 const response = await fetch(10 `https://api.yourapp.com/sync?last_pulled_at=${lastPulledAt || 0}`11 );12 if (!response.ok) throw new Error('Sync pull failed');13 const { changes, timestamp } = await response.json();14 return { changes, timestamp };15 },16 pushChanges: async ({ changes }) => {17 await fetch('https://api.yourapp.com/sync', {18 method: 'POST',19 headers: { 'Content-Type': 'application/json' },20 body: JSON.stringify(changes),21 });22 },23 migrationsEnabledAtVersion: 1,24 });25}
We chose WatermelonDB over AsyncStorage or MMKV for structured data because it handles sync conflicts gracefully — critical when users in the Philippines might make offline edits that need to merge once they reconnect. For simple key-value caching, MMKV remains faster (according to Callstack's benchmarks, MMKV reads are ~30x faster than AsyncStorage).
Ready to Transform Your Ecommerce Operations?
Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.
Step 6: Optimise List Rendering for Low-End Devices
Budget Android devices — the Samsung Galaxy A-series, Xiaomi Redmi, Realme C-series — make up the majority of smartphones across Southeast Asia according to Counterpoint Research Q4 2023 data. These devices have 2-4GB RAM and slower CPUs.
Replace FlatList with FlashList
1npm install @shopify/flash-list
1// screens/ProductList.tsx2import { FlashList } from '@shopify/flash-list';3import { AdaptiveImage } from '../components/AdaptiveImage';45export function ProductList({ products }) {6 const { pageSize } = useNetworkAwareConfig();78 return (9 <FlashList10 data={products}11 estimatedItemSize={120}12 renderItem={({ item }) => (13 <ProductCard item={item} />14 )}15 // Critical for low-RAM devices16 drawDistance={250} // Reduce from default 250 on poor networks17 overrideItemLayout={(layout, item) => {18 layout.size = 120; // Fixed height prevents recalculation19 }}20 // Maintain recycling pool for scroll performance21 estimatedFirstItemOffset={0}22 />23 );24}
Shopify's own benchmarks show FlashList achieves up to 5x fewer blank cells during fast scrolls compared to FlatList. On our Samsung Galaxy A04 test device, switching from FlatList to FlashList dropped frame drops during scroll from ~18 per second to ~3.
Avoid Anonymous Functions and Object Literals in renderItem
This is React Native 101, but it's the number-one performance issue we see in code reviews at Branch8:
1// BAD: Creates new object and function reference every render2<FlashList3 renderItem={({ item }) => (4 <View style={{ padding: 10, margin: 5 }}>5 <Text onPress={() => navigate(item.id)}>{item.title}</Text>6 </View>7 )}8/>910// GOOD: Memoised component with stable style references11const styles = StyleSheet.create({12 card: { padding: 10, margin: 5 },13});1415const ProductCard = React.memo(({ item, onPress }) => (16 <View style={styles.card}>17 <Text onPress={onPress}>{item.title}</Text>18 </View>19));2021// In parent:22const handlePress = useCallback((id) => navigate(id), [navigate]);
Step 7: Reduce APK Size for Faster Downloads
Google Play data from 2023 indicates that every 6MB increase in APK size reduces install conversion by 1% in emerging markets. When your users are on prepaid data plans in Indonesia or the Philippines, every megabyte costs them money.
Enable Proguard and Split ABIs
1// android/app/build.gradle2android {3 buildTypes {4 release {5 minifyEnabled true6 shrinkResources true7 proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'8 }9 }1011 splits {12 abi {13 enable true14 reset()15 include 'armeabi-v7a', 'arm64-v8a'16 universalApk false // Don't generate a universal APK17 }18 }19}
This alone can cut APK size by 30-40%. In our fintech project, the universal APK was 42MB; after ABI splitting, the arm64-v8a variant dropped to 24MB.
Use App Bundle Instead of APK
1cd android && ./gradlew bundleRelease
Google's Android App Bundle format with Play Delivery typically delivers 15% smaller downloads than optimised APKs according to Android Developers documentation.
Ready to Transform Your Ecommerce Operations?
Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.
A Branch8 Field Report: Vietnam Fintech App
When our Taipei and Ho Chi Minh City teams optimised a fintech app targeting Vietnamese users in Q3 2024, we applied every step in this guide over a six-week sprint. The starting metrics (tested on a Redmi 9A over Vietnamese 3G):
- Cold start TTI: 8.4 seconds
- Initial payload: 3.2 MB
- JS bundle: 7.1 MB
- Scroll frame drops: 22/sec on product list
After optimisation:
- Cold start TTI: 2.9 seconds (65% improvement)
- Initial payload: 480 KB (85% reduction via adaptive loading + gzip)
- JS bundle: 3.8 MB (46% reduction)
- Scroll frame drops: 2/sec (91% improvement)
The total engineering effort was approximately 180 person-hours across three developers — roughly USD 9,000 at Vietnamese senior developer rates. The ROI showed within four weeks: the client reported a 23% increase in daily active users and a 34% reduction in uninstalls, which they attributed directly to performance improvements in low-bandwidth regions.
Testing Your Optimisations Across APAC Markets
Don't trust emulator-only testing. Here's our testing matrix:
Physical Device Lab (Minimum Recommended)
- Philippines/Indonesia tier: Samsung Galaxy A04 or Redmi 9A (2GB RAM)
- Vietnam/Thailand tier: Samsung Galaxy A14 or Redmi Note 12 (4GB RAM)
- Taiwan/Singapore tier: Any mid-range device (6-8GB RAM)
- iOS: iPhone SE 3rd gen (baseline) and iPhone 12 (standard)
Automated Performance Regression with Maestro
1# .maestro/performance-check.yaml2appId: com.yourapp.mobile3---4- launchApp5- assertVisible: "Home Screen"6- scroll:7 direction: DOWN8 duration: 30009- assertVisible: "Product Card" # Verify content loaded10- stopApp
1# Run on CI with network throttle2maestro test .maestro/performance-check.yaml --device emulator-5554
Integrate this into your CI/CD pipeline. At Branch8, we gate releases on performance budgets — if TTI exceeds 3.5 seconds on the budget Android profile, the build fails.
Ready to Transform Your Ecommerce Operations?
Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.
What to Do Next
React Native performance optimisation for APAC low-bandwidth conditions is not a one-time task. Networks evolve, devices change, and your app's feature set grows. Here's your immediate action plan:
- This week: Set up the APAC throttle profiles from Step 1 and baseline your current metrics
- Next sprint: Implement the network-aware config hook (Step 3) and adaptive image loading (Step 4) — these deliver the highest ROI for the least effort
- This quarter: Migrate to FlashList, implement offline persistence, and establish performance budgets in CI
A trade-off to acknowledge honestly: these optimisations add complexity. The network-aware hooks, offline sync logic, and adaptive image loading increase your codebase size and testing surface. If your users are exclusively on stable broadband in Singapore, Sydney, or Taipei — and your analytics confirm this — you probably don't need the full stack described here. Start with bundle size reduction and FlashList, and layer on network adaptation only when your user data justifies it.
But if you're targeting the 400+ million smartphone users across Southeast Asia who regularly deal with patchy 3G connections and sub-USD-200 devices, this isn't optional. It's the difference between an app that gets uninstalled in frustration and one that earns daily active usage.
If your team needs help implementing these patterns — or if you need experienced React Native engineers who've already shipped under these conditions — get in touch with Branch8's engineering team.
Further Reading
- React Native Performance Overview — Official Docs
- Callstack's Ultimate Guide to React Native Optimization (2024)
- Shopify FlashList Documentation and Benchmarks
- WatermelonDB — High-Performance Local Database for React Native
- Opensignal Mobile Network Experience Reports — APAC
- HTTP Archive Web Almanac 2024 — Media Chapter
- Android Developers — Reduce APK Size
- Maestro — Mobile UI Testing Framework
FAQ
Enable Hermes, split APK by ABI architecture, use Android App Bundles, and run react-native-bundle-visualizer to identify and remove bloated dependencies. These steps typically reduce bundle size by 30-46%. Also strip console logs and enable Proguard's shrinkResources in your Gradle config.

About the Author
Elton Chan
Co-Founder, Second Talent & Branch8
Elton Chan is Co-Founder of Second Talent, a global tech hiring platform connecting companies with top-tier tech talent across Asia, ranked #1 in Global Hiring on G2 with a network of over 100,000 pre-vetted developers. He is also Co-Founder of Branch8, a Y Combinator-backed (S15) e-commerce technology firm headquartered in Hong Kong. With 14 years of experience spanning management consulting at Accenture (Dublin), cross-border e-commerce at Lazada Group (Singapore) under Rocket Internet, and enterprise platform delivery at Branch8, Elton brings a rare blend of strategy, technology, and operations expertise. He served as Founding Chairman of the Hong Kong E-Commerce Business Association (HKEBA), driving digital commerce education and cross-border collaboration across Asia. His work bridges technology, talent, and business strategy to help companies scale in an increasingly remote and digital world.