React Native Performance Optimization: Advanced Techniques
Master React Native performance optimization with techniques for reducing re-renders, optimizing images, improving list performance, and memory management.

React Native Performance Optimization: Advanced Techniques
React Native apps can suffer from performance issues if not optimized properly. This article explores advanced techniques to improve app performance, reduce memory usage, and create smooth user experiences.
Understanding React Native Performance
Key performance metrics:
- Frame Rate: Target 60 FPS
- Time to Interactive: First meaningful paint
- Memory Usage: Monitor for leaks
- Bundle Size: Minimize JavaScript bundle
Reducing Re-renders
1. Use React.memo
Prevent unnecessary re-renders:
import React, { memo } from 'react';
interface UserCardProps {
name: string;
email: string;
onPress: () => void;
}
const UserCard = memo(({ name, email, onPress }: UserCardProps) => {
return (
<TouchableOpacity onPress={onPress}>
<Text>{name}</Text>
<Text>{email}</Text>
</TouchableOpacity>
);
}, (prevProps, nextProps) => {
// Custom comparison
return prevProps.name === nextProps.name &&
prevProps.email === nextProps.email;
});
export default UserCard;2. useMemo for Expensive Calculations
import { useMemo } from 'react';
function ProductList({ products, filters }) {
const filteredProducts = useMemo(() => {
return products.filter(product => {
return filters.every(filter => filter(product));
});
}, [products, filters]);
return (
<FlatList
data={filteredProducts}
renderItem={({ item }) => <ProductItem product={item} />}
/>
);
}3. useCallback for Function References
import { useCallback, useState } from 'react';
function UserList({ users }) {
const [selectedId, setSelectedId] = useState(null);
const handleSelect = useCallback((id: string) => {
setSelectedId(id);
}, []); // Empty deps - function never changes
return (
<FlatList
data={users}
renderItem={({ item }) => (
<UserItem
user={item}
onSelect={handleSelect}
isSelected={item.id === selectedId}
/>
)}
/>
);
}Optimizing Lists
FlatList Best Practices
import { FlatList } from 'react-native';
function OptimizedList({ items }) {
return (
<FlatList
data={items}
renderItem={({ item }) => <ListItem item={item} />}
keyExtractor={(item) => item.id}
// Performance optimizations
removeClippedSubviews={true}
maxToRenderPerBatch={10}
updateCellsBatchingPeriod={50}
initialNumToRender={10}
windowSize={10}
// Memory optimization
getItemLayout={(data, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
})}
/>
);
}Virtualized Lists for Large Data
import { VirtualizedList } from 'react-native';
function LargeList({ items }) {
const getItem = (data: any, index: number) => data[index];
const getItemCount = () => items.length;
return (
<VirtualizedList
data={items}
renderItem={({ item }) => <ListItem item={item} />}
keyExtractor={(item) => item.id}
getItem={getItem}
getItemCount={getItemCount}
initialNumToRender={10}
maxToRenderPerBatch={10}
/>
);
}Image Optimization
1. Use Optimized Image Formats
import { Image } from 'react-native';
function OptimizedImage({ source, ...props }) {
return (
<Image
source={source}
resizeMode="cover"
// Use WebP on Android for better compression
defaultSource={require('./placeholder.png')}
// Progressive loading
progressiveRenderingEnabled={true}
{...props}
/>
);
}2. Lazy Loading Images
import { useState, useEffect } from 'react';
import { Image } from 'react-native';
function LazyImage({ uri, style }) {
const [loaded, setLoaded] = useState(false);
return (
<>
{!loaded && <Placeholder style={style} />}
<Image
source={{ uri }}
style={[style, { opacity: loaded ? 1 : 0 }]}
onLoad={() => setLoaded(true)}
/>
</>
);
}3. Image Caching
import FastImage from 'react-native-fast-image';
function CachedImage({ uri, style }) {
return (
<FastImage
source={{
uri,
priority: FastImage.priority.normal,
cache: FastImage.cacheControl.immutable,
}}
style={style}
resizeMode={FastImage.resizeMode.cover}
/>
);
}Memory Management
1. Clean Up Subscriptions
import { useEffect } from 'react';
import { AppState } from 'react-native';
function useAppState() {
useEffect(() => {
const subscription = AppState.addEventListener('change', (nextAppState) => {
if (nextAppState === 'background') {
// Clean up resources
}
});
return () => {
subscription.remove();
};
}, []);
}2. Avoid Memory Leaks
import { useEffect, useRef } from 'react';
function Component() {
const mountedRef = useRef(true);
useEffect(() => {
return () => {
mountedRef.current = false;
};
}, []);
const fetchData = async () => {
const data = await api.getData();
if (mountedRef.current) {
setData(data);
}
};
}Animation Performance
1. Use Native Driver
import { Animated } from 'react-native';
function AnimatedComponent() {
const fadeAnim = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.timing(fadeAnim, {
toValue: 1,
duration: 1000,
useNativeDriver: true, // Use native driver for better performance
}).start();
}, []);
return (
<Animated.View style={{ opacity: fadeAnim }}>
{/* Content */}
</Animated.View>
);
}2. Reanimated for Complex Animations
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
} from 'react-native-reanimated';
function SpringAnimation() {
const translateX = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => {
return {
transform: [{ translateX: translateX.value }],
};
});
const handlePress = () => {
translateX.value = withSpring(100);
};
return (
<Animated.View style={animatedStyle}>
<Button onPress={handlePress} title="Animate" />
</Animated.View>
);
}Bundle Size Optimization
1. Code Splitting
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<Loading />}>
<HeavyComponent />
</Suspense>
);
}2. Tree Shaking
// ❌ Bad: Imports entire library
import _ from 'lodash';
// ✅ Good: Import only what you need
import debounce from 'lodash/debounce';3. Metro Bundler Configuration
// metro.config.js
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true, // Enable inline requires
},
}),
},
};Performance Monitoring
1. Use Flipper
import { PerformanceMonitor } from 'react-native-performance-monitor';
function App() {
return (
<>
<PerformanceMonitor />
{/* Your app */}
</>
);
}2. Custom Performance Tracking
import { InteractionManager } from 'react-native';
function trackPerformance(name: string, fn: () => void) {
const start = Date.now();
InteractionManager.runAfterInteractions(() => {
fn();
const duration = Date.now() - start;
console.log(`${name} took ${duration}ms`);
});
}Best Practices
1. Avoid Inline Functions
// ❌ Bad: Creates new function on every render
<FlatList
data={items}
renderItem={({ item }) => <Item item={item} />}
/>
// ✅ Good: Stable function reference
const renderItem = useCallback(({ item }) => <Item item={item} />, []);
<FlatList data={items} renderItem={renderItem} />2. Optimize Style Objects
// ❌ Bad: Creates new object on every render
<View style={{ padding: 10, margin: 5 }} />
// ✅ Good: Use StyleSheet.create
const styles = StyleSheet.create({
container: {
padding: 10,
margin: 5,
},
});
<View style={styles.container} />3. Debounce User Input
import { useMemo } from 'react';
import { debounce } from 'lodash';
function SearchInput() {
const [query, setQuery] = useState('');
const debouncedSearch = useMemo(
() => debounce((text: string) => {
// Perform search
}, 300),
[]
);
return (
<TextInput
onChangeText={(text) => {
setQuery(text);
debouncedSearch(text);
}}
/>
);
}Conclusion
React Native performance optimization requires understanding rendering cycles, memory management, and platform-specific optimizations. By implementing these techniques—reducing re-renders, optimizing lists, managing memory, and monitoring performance—you can create smooth, responsive React Native applications that provide excellent user experiences.
References
Want more insights?
Subscribe to our newsletter or follow us for more updates on software development and team scaling.
Contact Us