Journal / Cross Platform and Mobile Development

Cross Platform and Mobile Development

React Native Performance: The Patterns That Make It Production Ready

React Native performance optimization involves identifying and eliminating the specific causes of frame drops, slow navigation, and janky animations in React Native apps. The main performance categories are JavaScript thread work (heavy computation, excessive rerenders), main thread contention (synchronous native calls, operations that trigger layout), network and data loading patterns (waterfall requests, missing loading states), and bundle size (large JavaScript payloads that increase startup time and parse time).

What you need to know

  • Excessive rerenders are the most common source of React Native performance problems. Use React DevTools Profiler to identify components that rerender unnecessarily.
  • Animations must run on the native thread to hit every frame. Use Reanimated 2+ for any animation that needs to be smooth regardless of JavaScript thread load.
  • Virtualize long lists. FlatList with windowSize and removeClippedSubviews is not optional for lists with more than 50 items.
  • Image optimization is disproportionately impactful. Memory pressure from large images causes the OS to kill background processes and slows the entire app.
  • Measure before optimizing. React Native has good profiling tooling (Hermes Profiler, React DevTools). Fix the measured bottleneck, not the suspected one.

The core argument

React Native apps that feel slow rarely have architectural problems. They have accumulated a set of specific, fixable patterns: components that rerender on every keystroke because a function reference is recreated inline, lists that render 500 items simultaneously instead of the 20 that are visible, images loaded at 4x display resolution because the API returns originals, and animations that run on the JavaScript thread and drop frames when network requests are in flight.

The diagnostic process matters more than the fix library. I have seen teams add memoization everywhere after reading a blog post about rerenders, without measuring which rerenders were actually expensive. Memoization has a cost: the useMemo and useCallback overhead is real, and memoizing components that rerender cheaply adds overhead without benefit. The correct approach is: measure which components render most frequently and most expensively using React DevTools Profiler, then apply memoization to those specific components with concrete measurements of improvement.

The patterns that consistently move performance metrics in my experience across React Native projects: moving navigation transition animations to Reanimated (eliminates frame drops during navigation), adding getItemLayout to FlatList for fixed height items (eliminates the layout calculation overhead on scroll), and deferring data fetching with InteractionManager.runAfterInteractions until navigation transitions complete. These three changes together have produced the most consistent improvement across different React Native projects.

Common mistakes

  1. Rendering all items in a list without virtualization. A ScrollView with 200 items renders all 200 components simultaneously, regardless of how many are visible. FlatList virtualizes rendering, keeping only visible items (plus a configurable buffer) in memory. Any list with more than 30 to 50 items should use FlatList, not ScrollView with mapped items.

  2. Creating new object and function references on every render. onPress={() => handlePress(item.id)} creates a new function on every render. style={{ marginTop: 10 }} creates a new object on every render. Both defeat React.memo and cause unnecessary child rerenders. Use useCallback for event handlers passed to components and StyleSheet.create for styles.

  3. Running synchronous storage reads on the main thread. AsyncStorage and SecureStore operations are asynchronous for a reason. Synchronous storage reads block the JavaScript thread. Initialize state from storage in a useEffect and show a loading state during initialization rather than blocking render on storage reads.

  4. Not using Hermes as the JavaScript engine. Hermes is the optimized JavaScript engine for React Native, with faster startup time and lower memory usage than V8. New React Native apps use Hermes by default. Older projects that have not migrated to Hermes are leaving significant startup performance on the table, particularly on Android.

  5. Debugging performance in the simulator instead of on a real device. The iOS simulator and Android emulator run on the Mac's CPU and do not reflect the memory constraints, CPU performance, or GPU limitations of physical devices. Performance that looks acceptable in the simulator may be unacceptable on a mid range Android device. Always validate performance on physical hardware, and specifically on a mid range Android device if Android is in the target market.

Where to start

  1. Profile with React DevTools Profiler to find rerender hotspots. Enable the profiler, perform the interaction that feels slow, and review the flame chart for components that rendered multiple times or took more than a few milliseconds. These are the optimization targets. Apply React.memo, useMemo, or useCallback to the specific components with measurable rerender cost.

  2. Audit image loading. Check the images displayed in the app and confirm they are sized appropriately for the display dimensions. Install expo-image or react-native-fast-image if not already in use. Verify that images rendered by FlatList are properly dequeued when scrolled out of view.

  3. Check FlatList configuration for long lists. For fixed height list items, add getItemLayout to eliminate the per item height calculation. Set windowSize to 5 to 7 (items to render above and below the viewport). Add removeClippedSubviews={true} to unmount items outside the viewport on Android. These three FlatList props together have a significant impact on scroll performance for long lists.

FAQ

Frequently asked

  • What is the React Native JavaScript thread and why does blocking it cause jank?
  • What causes excessive rerenders in React Native and how do you fix them?
  • What is Reanimated and why is it better than the built in Animated library?
  • How does image handling affect React Native performance?
  • What is InteractionManager and when should you use it?

Author

The work I take and why

I take work that compounds. I do not take work that is rework with extra steps. Yashveer Singh, founder of Yashveer Labs. If the topic on this page is what you are dealing with, the question is not whether it can be solved. It can. The question is whether you want to solve it once or four times. I am the person who solves it once.

Start the conversation See the work DM on Instagram