skills/ivantorresedge/molcajete.ai/react-native-performance

react-native-performance

SKILL.md

React Native Performance Skill

This skill covers performance optimization for React Native apps.

When to Use

Use this skill when:

  • App feels slow or janky
  • Lists scroll poorly
  • Animations stutter
  • Bundle size is too large
  • Memory usage is high

Core Principle

MEASURE FIRST - Profile before optimizing. Fix actual bottlenecks, not perceived ones.

Profiling Tools

React DevTools Profiler

// Enable profiling in development
if (__DEV__) {
  // React DevTools will show component render times
}

Flipper

# Install Flipper for advanced debugging
# https://fbflipper.com/

Performance Monitor

// Show FPS and memory in development
import { LogBox } from 'react-native';

if (__DEV__) {
  // Performance overlay available via shake gesture
}

Render Optimization

Avoid Inline Functions

// ❌ Bad: Creates new function every render
<Button onPress={() => handlePress(item.id)} />

// ✅ Good: Stable function reference
const handlePress = useCallback((id: string) => {
  console.log(id);
}, []);

<Button onPress={() => handlePress(item.id)} />

// ✅ Better: If possible, pass id differently
const handleItemPress = useCallback(() => {
  console.log(item.id);
}, [item.id]);

<Button onPress={handleItemPress} />

Memoize Components

import { memo } from 'react';

interface ItemCardProps {
  item: Item;
  onPress: (id: string) => void;
}

export const ItemCard = memo(function ItemCard({
  item,
  onPress,
}: ItemCardProps): React.ReactElement {
  return (
    <TouchableOpacity onPress={() => onPress(item.id)}>
      <Text>{item.name}</Text>
    </TouchableOpacity>
  );
});

useMemo for Expensive Computations

import { useMemo } from 'react';

function FilteredList({ items, filter }: Props): React.ReactElement {
  // Only recompute when items or filter change
  const filteredItems = useMemo(() => {
    return items.filter((item) =>
      item.name.toLowerCase().includes(filter.toLowerCase())
    );
  }, [items, filter]);

  return <FlashList data={filteredItems} {...rest} />;
}

useCallback for Event Handlers

import { useCallback } from 'react';

function ItemList(): React.ReactElement {
  const handlePress = useCallback((id: string) => {
    // Handle press
  }, []);

  const renderItem = useCallback(({ item }: { item: Item }) => (
    <ItemCard item={item} onPress={handlePress} />
  ), [handlePress]);

  return (
    <FlashList
      data={items}
      renderItem={renderItem}
      estimatedItemSize={80}
    />
  );
}

List Performance

Use FlashList Instead of FlatList

import { FlashList } from '@shopify/flash-list';

// ✅ FlashList is 10x faster than FlatList
<FlashList
  data={items}
  renderItem={({ item }) => <ItemCard item={item} />}
  estimatedItemSize={80}  // Required for FlashList
  keyExtractor={(item) => item.id}
/>

Optimize Item Rendering

// ✅ Use getItemType for different item layouts
<FlashList
  data={items}
  renderItem={({ item }) => {
    if (item.type === 'header') return <Header />;
    return <Item item={item} />;
  }}
  getItemType={(item) => item.type}
  estimatedItemSize={60}
/>

// ✅ Use overrideItemLayout for variable heights
<FlashList
  data={items}
  renderItem={renderItem}
  estimatedItemSize={60}
  overrideItemLayout={(layout, item) => {
    layout.size = item.type === 'header' ? 100 : 60;
  }}
/>

Image Optimization

Use expo-image

import { Image } from 'expo-image';

// ✅ expo-image is faster than React Native Image
<Image
  source={{ uri: imageUrl }}
  style={{ width: 200, height: 200 }}
  contentFit="cover"
  transition={200}
  cachePolicy="memory-disk"
/>

Optimize Image Size

// ✅ Request appropriately sized images
const imageUrl = `${baseUrl}?w=400&h=400`;

// ✅ Use blurhash for placeholders
<Image
  source={{ uri: imageUrl }}
  placeholder={blurhash}
  transition={200}
/>

Animation Optimization

Use Reanimated

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

// ✅ Animations run on UI thread
function AnimatedBox(): React.ReactElement {
  const scale = useSharedValue(1);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));

  return <Animated.View style={animatedStyle} />;
}

Avoid JS-Driven Animations

// ❌ Bad: JS thread animations
const [scale, setScale] = useState(1);
Animated.timing(scale, { toValue: 2 }).start();

// ✅ Good: Reanimated (runs on UI thread)
const scale = useSharedValue(1);
scale.value = withSpring(2);

Bundle Size Optimization

Analyze Bundle

npx react-native-bundle-visualizer

Optimize Imports

// ❌ Bad: Imports entire library
import { format } from 'date-fns';

// ✅ Good: Import only what you need
import format from 'date-fns/format';

// ❌ Bad: Import all icons
import * as Icons from 'lucide-react-native';

// ✅ Good: Import specific icons
import { Home, Search } from 'lucide-react-native';

Replace Heavy Libraries

// Replace moment.js (300KB) with:
// - date-fns (tree-shakeable)
// - dayjs (2KB)

// Replace lodash with:
// - Individual lodash functions
// - Native JavaScript methods

Memory Management

Clean Up Effects

useEffect(() => {
  const subscription = eventEmitter.subscribe(handler);

  // ✅ Always clean up subscriptions
  return () => {
    subscription.unsubscribe();
  };
}, []);

Avoid Memory Leaks

// ✅ Cancel async operations
useEffect(() => {
  let isMounted = true;

  fetchData().then((data) => {
    if (isMounted) {
      setData(data);
    }
  });

  return () => {
    isMounted = false;
  };
}, []);

Common Pitfalls

Avoid

// ❌ Inline styles (creates new object every render)
<View style={{ padding: 10 }} />

// ❌ Anonymous functions in render
<Button onPress={() => handlePress()} />

// ❌ FlatList for long lists
<FlatList data={longList} />

// ❌ Unoptimized images
<Image source={{ uri: fullSizeImage }} />

Prefer

// ✅ NativeWind or StyleSheet
<View className="p-4" />

// ✅ useCallback for handlers
const handlePress = useCallback(() => {...}, []);

// ✅ FlashList for long lists
<FlashList data={longList} estimatedItemSize={80} />

// ✅ Optimized images with expo-image
<Image source={{ uri: optimizedImage }} cachePolicy="memory-disk" />

Performance Checklist

  • Use FlashList instead of FlatList
  • Memoize expensive computations
  • Use useCallback for event handlers
  • Use React.memo for list items
  • Use Reanimated for animations
  • Use expo-image for images
  • Analyze and optimize bundle size
  • Clean up effects and subscriptions
  • Profile before optimizing

Notes

  • Profile on actual devices, not simulators
  • Focus on 60fps for smooth experience
  • Monitor memory usage
  • Test on lower-end devices
  • Use New Architecture for better performance
Weekly Installs
2
GitHub Stars
2
First Seen
Jan 23, 2026
Installed on
opencode2
gemini-cli2
antigravity2
claude-code2
mcpjam1
iflow-cli1