← Back to Blog
React NativePerformanceMobile

How I reduced app load time by 30% in React Native

December 10, 20252 min read

When I joined Ehya Education Service, the Classmate app had a noticeable load delay — users on mid-range Android devices waited over 3 seconds before seeing the home screen. Over a few months, we brought that down by 30%. Here's exactly what made the difference.

1. Profiling first, optimizing second

The most common mistake is optimizing blind. I started with the React Native Performance Monitor (shake device → Show Perf Monitor) to establish baselines, then used Flipper with the Hermes Debugger to trace JS thread bottlenecks.

Key finding: 60% of our startup delay was a synchronous data fetch inside useEffect on the root screen — blocking render with no skeleton UI.

2. Lazy loading heavy screens

React Native's default bundler loads all your routes eagerly. Using React.lazy with Suspense wrapped around non-critical screens (Settings, Profile, deep course pages) cut our initial JS bundle parse time significantly.

const Settings = React.lazy(() => import('./screens/Settings'))

// In your navigator:
<Suspense fallback={<LoadingSkeleton />}>
  <Settings />
</Suspense>

3. Moving data fetching out of render

We moved our initial API call from useEffect (which runs after first render) to React Query's prefetchQuery at the navigation level — so data is in cache before the screen mounts.

// In the tab navigator's onPress
await queryClient.prefetchQuery({
  queryKey: ['home-feed'],
  queryFn: fetchHomeFeed,
})

4. Image optimization

Unoptimized images were the silent killer. We switched to expo-image (which uses Glide on Android, SDWebImage on iOS) and added contentFit="cover" with explicit width/height props everywhere — eliminating layout thrash.

Results

| Metric | Before | After | |--------|--------|-------| | Time to interactive | 3.2s | 2.1s | | JS bundle size | 4.1MB | 2.8MB | | ANR rate (Android) | 1.4% | 0.3% |

The 30% figure came from comparing cold-start TTI across 200 real-device sessions in our staging environment.

Performance work is never done — next up is investigating Hermes bytecode precompilation and investigating whether migrating to the New Architecture gives us a meaningful boost on the JS thread.