Common React Native Performance Mistakes (And How to Fix Them)

Published on December 16, 2025 • 8 min read

After 8+ years of building React Native applications, I've seen the same performance mistakes repeated across countless projects. The good news? Most of these issues are easy to fix once you know what to look for. In this guide, I'll walk you through the most common performance pitfalls and show you exactly how to solve them.

1. Unnecessary Re-renders: The Silent Performance Killer

This is by far the most common mistake I see. Every time a component re-renders unnecessarily, your app does wasted work, draining battery and causing frame drops.

The Problem

// ❌ Bad: This component re-renders on every parent update
const UserCard = ({ user, onPress }) => {
  return (
    <TouchableOpacity onPress={() => onPress(user.id)}>
      <Text>{user.name}</Text>
    </TouchableOpacity>
  );
};

Every parent re-render recreates that onPress arrow function, causing UserCard to re-render even when the user data hasn't changed.

The Solution

// ✅ Good: Memoized component with stable callbacks
const UserCard = React.memo(({ user, onPress }) => {
  return (
    <TouchableOpacity onPress={onPress}>
      <Text>{user.name}</Text>
    </TouchableOpacity>
  );
});

// In parent component
const handlePress = useCallback((userId) => {
  navigation.navigate('UserDetail', { userId });
}, [navigation]);
Pro Tip: Use the React DevTools Profiler to identify components that re-render frequently. You'll be surprised how many unnecessary renders happen in a typical app.

2. Heavy Computations on the Main Thread

React Native runs JavaScript on a single thread. Block that thread with heavy computations, and your UI freezes.

The Problem

// ❌ Bad: Heavy filtering blocks UI
const SearchResults = ({ data, query }) => {
  const filtered = data.filter(item =>
    item.title.toLowerCase().includes(query.toLowerCase())
  );

  return <FlatList data={filtered} .../>;
};

With 10,000+ items, this filter operation runs on every keystroke, freezing your search input.

The Solution

// ✅ Good: Memoized computation
const SearchResults = ({ data, query }) => {
  const filtered = useMemo(() => {
    return data.filter(item =>
      item.title.toLowerCase().includes(query.toLowerCase())
    );
  }, [data, query]);

  return <FlatList data={filtered} .../>;
};

For truly heavy computations, consider moving them to a native module or using libraries like react-native-multithreading.

3. FlatList Misconfiguration

FlatList is powerful, but misconfigured FlatLists are a top cause of scrolling jank.

Critical Props You're Probably Missing

// ✅ Optimized FlatList
<FlatList
  data={items}
  renderItem={renderItem}
  keyExtractor={item => item.id}

  // Performance optimizations
  removeClippedSubviews={true}
  maxToRenderPerBatch={10}
  updateCellsBatchingPeriod={50}
  initialNumToRender={10}
  windowSize={5}

  // Prevent layout thrashing
  getItemLayout={(data, index) => ({
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  })}
/>

What Each Prop Does

  • removeClippedSubviews: Unmounts offscreen components (huge memory savings)
  • maxToRenderPerBatch: Limits renders per scroll event (smoother scrolling)
  • windowSize: Controls how many screen heights to render (balance memory vs scroll responsiveness)
  • getItemLayout: Skips expensive layout calculations if all items are same height

4. Animated Image Loading Without Optimization

Images are often the biggest performance bottleneck in mobile apps. Large, unoptimized images can tank your app's performance.

The Problem

// ❌ Bad: Full-resolution image from API
<Image
  source={{ uri: 'https://api.example.com/image/4k-photo.jpg' }}
  style={{ width: 100, height: 100 }}
/>

You're downloading a 5MB image just to display it at 100x100 pixels. Wasteful!

The Solution

// ✅ Good: Use FastImage with proper sizing
import FastImage from 'react-native-fast-image';

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

Best practices:

  • Use react-native-fast-image for better caching and performance
  • Request appropriately sized images from your API
  • Use WebP format (80% smaller than PNG)
  • Implement progressive image loading for large images

5. Navigation Performance Issues

Screen transitions should be smooth and instant. If they're not, you're likely making one of these mistakes.

Lazy Load Heavy Screens

// ✅ Good: Lazy load heavy imports
const HeavyScreen = lazy(() => import('./screens/HeavyScreen'));

const Stack = createNativeStackNavigator();

function AppNavigator() {
  return (
    <Stack.Navigator>
      <Stack.Screen name="Home" component={HomeScreen} />
      <Stack.Screen name="Heavy" component={HeavyScreen} />
    </Stack.Navigator>
  );
}

Avoid Work During Navigation

// ❌ Bad: Heavy operation blocks navigation
navigation.navigate('Details');
performHeavyOperation();

// ✅ Good: Defer heavy work
navigation.navigate('Details');
requestAnimationFrame(() => {
  performHeavyOperation();
});

6. Console.log in Production

This sounds trivial, but leaving console.logs in production can slow your app by 30-40%.

The Solution

// In your entry file (index.js)
if (!__DEV__) {
  console.log = () => {};
  console.warn = () => {};
  console.error = () => {};
}

Or use a proper logging library like react-native-logs that automatically disables in production.

Real-World Impact: A Case Study

On a recent project, we applied these optimizations to an e-commerce app with 50,000+ products:

  • Before: 28 FPS average, 3.2s screen transition time
  • After: 58 FPS average, 0.4s screen transition time
  • Result: 2x improvement in user engagement, 40% reduction in crash rates

Measuring Performance

You can't improve what you don't measure. Use these tools to track your progress:

  • Flipper: React DevTools, Network inspector, Performance monitor
  • React Native Performance Monitor: Built-in FPS counter (shake device → Show Perf Monitor)
  • Xcode Instruments / Android Profiler: Deep native performance analysis

Conclusion

React Native performance issues are almost always fixable. The key is knowing what to look for and applying the right optimizations systematically. Start with the low-hanging fruit (removing console.logs, memoizing components) and work your way up to more advanced optimizations.

Remember: 60 FPS should be your baseline, not your goal. Users expect smooth, native-feeling apps, and these optimizations will get you there.

Need help optimizing your React Native app? At ThreadCode, we've optimized dozens of production apps to achieve 60+ FPS. Get in touch for a free performance audit.