React Native Performance

My React Native App is Slow: How to Find and Fix Performance Bottlenecks

October 28, 202511 min read
React Native Performance

React Native apps can suffer from performance bottlenecks that make them feel slow and unresponsive. This comprehensive guide identifies the most common performance issues in React Native apps and provides practical solutions to fix them, ensuring your app runs smoothly at 60 FPS on both iOS and Android.

Top 7 Performance Bottlenecks

1. Heavy JavaScript Thread Blocking

The JavaScript thread handles all your app logic. When it's blocked, the UI freezes.

// ❌ Bad - Blocks JS thread
const processData = (items) => {
  return items.map(item => heavyComputation(item));
};

// ✅ Good - Use InteractionManager
import { InteractionManager } from 'react-native';

const processData = async (items) => {
  await InteractionManager.runAfterInteractions();
  return items.map(item => heavyComputation(item));
};

2. Unnecessary Re-renders

Components re-rendering too often is a major performance killer.

// ❌ Bad - Re-renders on every parent update
const UserCard = ({ user }) => {
  return <View><Text>{user.name}</Text></View>;
};

// ✅ Good - Memoized component
const UserCard = React.memo(({ user }) => {
  return <View><Text>{user.name}</Text></View>;
}, (prevProps, nextProps) => {
  return prevProps.user.id === nextProps.user.id;
});

3. Large List Rendering

Rendering hundreds of items at once tanks performance.

// ❌ Bad - Renders all items
{items.map(item => <ItemCard key={item.id} data={item} />)}

// ✅ Good - Virtualized list
<FlatList
  data={items}
  renderItem={({ item }) => <ItemCard data={item} />}
  keyExtractor={item => item.id}
  removeClippedSubviews={true}
  maxToRenderPerBatch={10}
  windowSize={5}
/>

4. Image Loading Issues

Large, unoptimized images cause memory spikes and slow rendering.

// ❌ Bad - No optimization
<Image source={{ uri: 'https://example.com/large-image.jpg' }} />

// ✅ Good - Resized and cached
import FastImage from 'react-native-fast-image';

<FastImage
  source={{
    uri: 'https://example.com/large-image.jpg',
    priority: FastImage.priority.normal,
  }}
  resizeMode={FastImage.resizeMode.cover}
  style={{ width: 200, height: 200 }}
/>

5. Bridge Congestion

Too many messages between JavaScript and native code slows everything down.

// ❌ Bad - Calls native on every scroll
<ScrollView onScroll={(e) => {
  NativeModule.trackScroll(e.nativeEvent.contentOffset.y);
}}>

// ✅ Good - Batch updates
const scrollY = useRef(0);
<ScrollView onScroll={(e) => {
  scrollY.current = e.nativeEvent.contentOffset.y;
}} onScrollEndDrag={() => {
  NativeModule.trackScroll(scrollY.current);
}}>

6. Slow Animations

Running animations on the JS thread causes jank.

// ❌ Bad - Runs on JS thread
const fadeAnim = useRef(new Animated.Value(0)).current;
Animated.timing(fadeAnim, {
  toValue: 1,
  useNativeDriver: false // Bad!
}).start();

// ✅ Good - Runs on native thread
Animated.timing(fadeAnim, {
  toValue: 1,
  useNativeDriver: true // Good!
}).start();

7. Memory Leaks

Uncleared timers and listeners cause memory to grow until the app crashes.

useEffect(() => {
  const interval = setInterval(() => {
    fetchData();
  }, 5000);

  // ✅ Always clean up!
  return () => clearInterval(interval);
}, []);

Performance Monitoring Tools

  • React DevTools Profiler - Identify slow components
  • Flipper - Debug bridge traffic and memory usage
  • Xcode Instruments / Android Profiler - Native performance analysis
  • Logtrics - Monitor real-world app performance metrics

Quick Wins Checklist

  • ✅ Enable Hermes engine (up to 2x faster startup)
  • ✅ Use FlatList instead of ScrollView for long lists
  • ✅ Add React.memo() to frequently-rendered components
  • ✅ Enable useNativeDriver for all animations
  • ✅ Optimize images (use WebP, resize appropriately)
  • ✅ Lazy load screens and heavy components
  • ✅ Profile in Release mode, not Debug

Advanced: Profiling with Logtrics

Monitor real-world performance in production with Logtrics:

  • Frame Rate Tracking: See actual FPS across millions of devices, not just your test phone
  • Memory Profiling: Track heap usage, detect leaks, identify memory spikes
  • Bridge Performance: Monitor JavaScript-to-native bridge latency
  • Render Performance: Identify slow screens and components
  • Cold Start Metrics: Track app launch time and first interaction

Performance Best Practices Summary

Before Each Release

  • • Profile in Release mode
  • • Test on slow devices
  • • Measure impact of changes
  • • Check memory baseline

In Production

  • • Monitor real user metrics
  • • Watch for regressions
  • • Set performance alerts
  • • Track user impact

Conclusion

React Native can deliver native-like performance when optimized correctly. By addressing these seven common bottlenecks—JavaScript thread blocking, unnecessary re-renders, list rendering, image loading, bridge congestion, animations, and memory leaks—you can dramatically improve your app's performance. Use the profiling tools and monitoring strategies mentioned above to identify issues specific to your app, and always test on real devices in Release mode for accurate results. With Logtrics, you can monitor performance across your entire user base and catch regressions before they impact users.

Get Started with Logtrics

Comprehensive mobile app logging, crash reporting, and performance monitoring.

Get Started →