Identifying Performance Bottlenecks

Common issues include excessive re-renders, heavy bridge traffic, and large bundle sizes. Identify these before optimizing. Performance problems in React Native typically manifest as:

  • Slow Initial Load: Large JavaScript bundle takes time to parse and execute
  • Janky Scrolling: Dropped frames during list scrolling or animations
  • Memory Leaks: App crashes after extended use
  • Slow Navigation: Delays when switching screens
  • Bridge Delays: Lag when calling native modules or APIs

Use React Native's Performance Monitor (shake device → Dev Menu → Perf Monitor) to identify bottlenecks. Look for frame drops below 60 FPS, JavaScript thread being blocked, and memory usage spikes.

Render Optimization Techniques

Use React.memo, useMemo, and useCallback to prevent unnecessary re-renders. Avoid anonymous functions in render props. Here's when to use each:

  • React.memo: Wrap components that receive stable props but re-render frequently
  • useMemo: Cache expensive calculations that depend on specific inputs
  • useCallback: Stabilize function references passed to child components

Avoid creating new objects or arrays in render: <Component style={{color: 'red'}} /> creates a new object every render. Extract static values outside the component or use useMemo.

Premature optimization is the root of all evil—profile first, then optimize what actually matters.

Optimizing FlatList Performance

Optimize FlatList: use distinct keys, implement getItemLayout, adjust windowSize, and use memoized renderItem components. FlatList is React Native's most performance-critical component for displaying lists:

  • getItemLayout: If items have fixed height, provide this to skip measurement
  • windowSize: Render more items off-screen for smoother scrolling (default: 21)
  • initialNumToRender: Render fewer items initially for faster first paint
  • maxToRenderPerBatch: Limit items rendered per batch
  • removeClippedSubviews: Unmount off-screen views to save memory
  • updateCellsBatchingPeriod: Delay between render batches

For lists with hundreds of items, consider virtualization libraries or pagination. Never use ScrollView with map() for long lists—it renders everything at once.

Minimizing Bridge Traffic

Reduce passes over the JS-Native bridge. Batch state updates. Use the new Architecture (Fabric) for direct communication. The bridge is React Native's biggest performance bottleneck:

  • Batch Updates: Multiple setState calls in one event loop are batched automatically
  • Avoid Bridge Calls in Loops: Accumulate data, then make one bridge call
  • Use Native Driver: Animated API can run animations on the native thread
  • New Architecture: Fabric eliminates the bridge for UI updates
  • JSI: JavaScript Interface enables synchronous native calls

Each bridge call has overhead. Minimize by batching operations, using native modules for heavy computation, and leveraging the New Architecture when possible.

Leveraging the Hermes Engine

Enable Hermes for faster startup, smaller app size, and reduced memory usage. It pre-compiles JS to bytecode. Hermes is Facebook's JavaScript engine optimized for React Native:

  • Faster Startup: Pre-compiled bytecode eliminates parsing time
  • Smaller Bundles: Bytecode is more compact than JavaScript source
  • Lower Memory: Optimized garbage collection reduces memory footprint
  • Better Performance: Optimized for mobile constraints

Enable Hermes in your android/app/build.gradle and ios/Podfile. It's enabled by default in React Native 0.70+. Measure improvements using React Native's startup profiler.

Using Native Modules for Heavy Computation

Offload heavy computation to native modules (C++/Swift/Kotlin). Use JSI (JavaScript Interface) for synchronous native calls. When to go native:

  • Image Processing: Filters, compression, cropping
  • Video Encoding: Recording, compression, format conversion
  • Cryptography: Encryption, hashing, secure storage
  • Complex Algorithms: Machine learning inference, data parsing
  • Background Tasks: Download management, sync operations

JSI allows synchronous calls, eliminating bridge overhead. Use libraries like react-native-reanimated for animations running entirely on the UI thread. Remember: premature native code adds complexity—only optimize when profiling shows it's necessary.

Profiling Tools and Techniques

Use Flipper, React DevTools, and the Performance Monitor. Trace rendering updates and bridge traffic. Your profiling toolkit should include:

  • Flipper: Network inspector, layout inspector, logs, crash reporter
  • React DevTools: Component tree, props, state, profiling render times
  • Performance Monitor: FPS, JS FPS, memory usage (built into React Native)
  • Chrome DevTools: JavaScript debugging, source maps, breakpoints
  • Native Profilers: Xcode Instruments (iOS), Android Profiler

Profile regularly during development, not just when performance is obviously bad. Small optimizations compound—saving 10ms per render can make a huge difference in user experience. Use the Performance tab in React DevTools to identify components causing unnecessary re-renders.