From harness-claude
Optimizes React Native app performance with Hermes engine, memoization, lazy loading, expo-image optimization, and profiling for sluggish scrolling, slow startups (>2s), low FPS, and large bundles.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Optimize React Native app performance with profiling, memoization, lazy loading, and native thread management
Provides React Native performance optimization guidelines for FPS, TTI, bundle size, memory leaks, re-renders, and animations. Guides Hermes optimization, JS thread blocking, bridge overhead, FlashList, native modules, and jank debugging.
Optimizes Expo React Native apps using 54 rules across launch time, bundle size, lists, images, navigation, animations, re-renders, and memory. Apply when writing, reviewing, or refactoring for mobile performance.
Optimizes React Native app performance via FlatList tweaks, React.memo/useMemo, FastImage caching, bundle size reduction, and profiling techniques.
Share bugs, ideas, or general feedback.
Optimize React Native app performance with profiling, memoization, lazy loading, and native thread management
// app.json
{
"expo": {
"jsEngine": "hermes"
}
}
React.memo, useMemo, and useCallback.// Memoize components that receive the same props frequently
const ProductCard = memo(function ProductCard({ product }: { product: Product }) {
return (
<View>
<Image source={{ uri: product.imageUrl }} style={styles.image} />
<Text>{product.name}</Text>
<Text>${product.price}</Text>
</View>
);
});
// Memoize expensive computations
function OrderList({ orders, filter }: Props) {
const filteredOrders = useMemo(
() => orders.filter((o) => o.status === filter).sort((a, b) => b.date - a.date),
[orders, filter]
);
// Stable callback reference for child components
const handlePress = useCallback(
(orderId: string) => {
navigation.navigate('OrderDetail', { orderId });
},
[navigation]
);
return <FlatList data={filteredOrders} renderItem={/* ... */} />;
}
useCallback for FlatList renderItem and keyExtractor.const renderItem = useCallback(
({ item }: { item: Order }) => <OrderCard order={item} onPress={handlePress} />,
[handlePress]
);
const keyExtractor = useCallback((item: Order) => item.id, []);
<FlatList data={orders} renderItem={renderItem} keyExtractor={keyExtractor} />;
import { lazy, Suspense } from 'react';
const HeavyChart = lazy(() => import('./components/HeavyChart'));
function Dashboard() {
return (
<Suspense fallback={<ChartSkeleton />}>
<HeavyChart data={chartData} />
</Suspense>
);
}
import { Image } from 'expo-image';
// expo-image provides caching, blurhash placeholders, and memory management
<Image
source={{ uri: product.imageUrl }}
placeholder={{ blurhash: product.blurhash }}
contentFit="cover"
transition={200}
style={styles.image}
recyclingKey={product.id}
/>;
expo-image instead of React Native's Image for better caching# Enable the React DevTools profiler
npx react-devtools
// Bad — imports the entire library
import { format, parse, addDays, subDays, isAfter } from 'date-fns';
// Good — import only what you need (tree-shakeable)
import format from 'date-fns/format';
import addDays from 'date-fns/addDays';
// Check bundle size
npx expo-doctor --check-dependencies
expo-splash-screen to keep the splash visible until critical data loadsexpo-font and expo-assetimport * as SplashScreen from 'expo-splash-screen';
SplashScreen.preventAutoHideAsync();
function App() {
const [ready, setReady] = useState(false);
useEffect(() => {
async function prepare() {
await loadFonts();
await loadCriticalData();
setReady(true);
}
prepare();
}, []);
const onLayoutRootView = useCallback(async () => {
if (ready) await SplashScreen.hideAsync();
}, [ready]);
if (!ready) return null;
return <View onLayout={onLayoutRootView}>{/* app content */}</View>;
}
Avoid bridge traffic for animations. Use Reanimated (UI thread) instead of Animated (JS thread). Use useAnimatedStyle instead of style objects that depend on animated values.
Monitor performance in production with tools like Sentry Performance or custom metrics.
React Native threading model: React Native has three threads — the JS thread (runs your React code), the UI/Main thread (renders native views), and the Shadow thread (calculates layout with Yoga). Performance problems usually fall into: JS thread overload (expensive re-renders), bridge congestion (too much data crossing), or main thread blocking (synchronous native calls).
Common re-render causes:
memo)useMemo)useCallback)Memory management:
useEffect returnNew Architecture (Fabric + TurboModules): The new architecture removes the bridge, enabling synchronous communication between JS and native. It improves performance for interop-heavy operations. Available in Expo SDK 51+.
https://reactnative.dev/docs/performance