React Native performance optimization techniques. Use when optimizing app performance.
This skill inherits all available tools. When active, it can use any tool Claude has access to.
This skill covers performance optimization for React Native apps.
Use this skill when:
MEASURE FIRST - Profile before optimizing. Fix actual bottlenecks, not perceived ones.
// Enable profiling in development
if (__DEV__) {
// React DevTools will show component render times
}
# Install Flipper for advanced debugging
# https://fbflipper.com/
// Show FPS and memory in development
import { LogBox } from 'react-native';
if (__DEV__) {
// Performance overlay available via shake gesture
}
// ❌ 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} />
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>
);
});
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} />;
}
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}
/>
);
}
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}
/>
// ✅ 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;
}}
/>
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"
/>
// ✅ Request appropriately sized images
const imageUrl = `${baseUrl}?w=400&h=400`;
// ✅ Use blurhash for placeholders
<Image
source={{ uri: imageUrl }}
placeholder={blurhash}
transition={200}
/>
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} />;
}
// ❌ 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);
npx react-native-bundle-visualizer
// ❌ 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 moment.js (300KB) with:
// - date-fns (tree-shakeable)
// - dayjs (2KB)
// Replace lodash with:
// - Individual lodash functions
// - Native JavaScript methods
useEffect(() => {
const subscription = eventEmitter.subscribe(handler);
// ✅ Always clean up subscriptions
return () => {
subscription.unsubscribe();
};
}, []);
// ✅ Cancel async operations
useEffect(() => {
let isMounted = true;
fetchData().then((data) => {
if (isMounted) {
setData(data);
}
});
return () => {
isMounted = false;
};
}, []);
// ❌ 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 }} />
// ✅ 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" />