← back to blog
12 min read

React Native Performance: Practical Optimization Tips

Battle-tested techniques to make your React Native app faster, from reducing re-renders to optimizing images and lists.

React NativePerformanceMobile

After years of building React Native apps, I've collected optimization techniques that actually matter. Skip the premature optimization - focus on these high-impact areas.

Measure First

Before optimizing, measure. React Native has built-in tools:

// Enable performance monitor
import { PerformanceMonitor } from 'react-native';

// In development, shake device and enable "Perf Monitor"

Look for:

  • JS frame rate (should be 60fps)
  • UI frame rate (should be 60fps)
  • RAM usage

1. Fix Re-renders

The biggest performance killer. Use React DevTools Profiler to find components re-rendering unnecessarily.

Use React.memo wisely

const ExpensiveComponent = React.memo(({ data }) => {
  // Only re-renders when data changes
  return <ComplexView data={data} />;
});

Stabilize callbacks with useCallback

// Bad - creates new function every render
<Button onPress={() => handlePress(id)} />

// Good - stable reference const handlePressCallback = useCallback(() => { handlePress(id); }, [id]); <Button onPress={handlePressCallback} />

2. Optimize Lists

FlatList is powerful but needs tuning:

<FlatList
  data={items}
  renderItem={renderItem}
  keyExtractor={item => item.id}
  // Performance props
  removeClippedSubviews={true}
  maxToRenderPerBatch={10}
  windowSize={5}
  initialNumToRender={10}
  getItemLayout={(data, index) => ({
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  })}
/>

For complex items, extract to a memoized component:

const ListItem = React.memo(({ item, onPress }) => (
  <TouchableOpacity onPress={() => onPress(item.id)}>
    <Text>{item.title}</Text>
  </TouchableOpacity>
));

3. Image Optimization

Images are often the slowest part:

// Use FastImage for caching
import FastImage from 'react-native-fast-image';

<FastImage source={{ uri: imageUrl, priority: FastImage.priority.normal }} resizeMode={FastImage.resizeMode.cover} />

Also:

  • Serve correctly sized images (don't download 4K for a thumbnail)
  • Use WebP format when possible
  • Implement progressive loading for large images

4. Reduce Bundle Size

Large bundles = slow startup:

# Analyze your bundle
npx react-native-bundle-visualizer

Common fixes:

  • Replace moment.js with date-fns
  • Use lodash-es and import only what you need
  • Lazy load heavy screens

5. Native Driver Animations

Always use native driver when possible:

Animated.timing(fadeAnim, {
  toValue: 1,
  duration: 300,
  useNativeDriver: true, // This is key
}).start();

Native driver limitations:

  • Only works with transform and opacity
  • Can't animate layout properties (width, height, margin)
For complex animations, consider Reanimated 2:

import Animated, {
  useSharedValue,
  withSpring
} from 'react-native-reanimated';

const offset = useSharedValue(0); offset.value = withSpring(100);

Real Impact

Applying these to a client app:

  • Startup time: 3.2s → 1.8s
  • List scrolling: 45fps → 60fps
  • Memory usage: 180MB → 120MB
Performance optimization is iterative. Measure, fix the biggest issue, repeat.