jutsu-react-native:react-native-performance

React Native Performance

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "jutsu-react-native:react-native-performance" with this command: npx skills add thebushidocollective/han/thebushidocollective-han-jutsu-react-native-react-native-performance

React Native Performance

Use this skill when optimizing React Native applications for better performance, faster load times, and smoother user experiences.

Key Concepts

List Performance

Optimize FlatList for large datasets:

import React, { useCallback } from 'react'; import { FlatList, View, Text, StyleSheet } from 'react-native';

interface Item { id: string; title: string; }

const ItemComponent = React.memo(({ item }: { item: Item }) => ( <View style={styles.item}> <Text>{item.title}</Text> </View> ));

function OptimizedList({ data }: { data: Item[] }) { const renderItem = useCallback( ({ item }: { item: Item }) => <ItemComponent item={item} />, [] );

const keyExtractor = useCallback((item: Item) => item.id, []);

const getItemLayout = useCallback( (data: any, index: number) => ({ length: 80, // Fixed item height offset: 80 * index, index, }), [] );

return ( <FlatList data={data} renderItem={renderItem} keyExtractor={keyExtractor} getItemLayout={getItemLayout} // Performance optimizations removeClippedSubviews={true} maxToRenderPerBatch={10} updateCellsBatchingPeriod={50} initialNumToRender={10} windowSize={5} /> ); }

const styles = StyleSheet.create({ item: { height: 80, padding: 16, borderBottomWidth: 1, borderBottomColor: '#eee', }, });

Component Memoization

Use React.memo and useMemo:

import React, { useMemo } from 'react'; import { View, Text } from 'react-native';

interface UserCardProps { user: { id: string; name: string; email: string; }; onPress: () => void; }

// Memoize component to prevent unnecessary re-renders const UserCard = React.memo(({ user, onPress }: UserCardProps) => { return ( <View> <Text>{user.name}</Text> <Text>{user.email}</Text> </View> ); });

// Memoize expensive computations function UserList({ users }: { users: User[] }) { const sortedUsers = useMemo(() => { return [...users].sort((a, b) => a.name.localeCompare(b.name)); }, [users]);

return ( <View> {sortedUsers.map(user => ( <UserCard key={user.id} user={user} onPress={() => {}} /> ))} </View> ); }

Image Optimization

Optimize image loading and caching:

import React from 'react'; import { Image, View } from 'react-native'; import FastImage from 'react-native-fast-image';

// Use FastImage for better performance function OptimizedImage({ uri }: { uri: string }) { return ( <FastImage style={{ width: 200, height: 200 }} source={{ uri, priority: FastImage.priority.normal, cache: FastImage.cacheControl.immutable, }} resizeMode={FastImage.resizeMode.cover} /> ); }

// Lazy load images function LazyImage({ uri }: { uri: string }) { return ( <Image source={{ uri }} style={{ width: 200, height: 200 }} resizeMode="cover" loadingIndicatorSource={require('./placeholder.png')} /> ); }

Best Practices

Use useCallback for Event Handlers

Prevent unnecessary re-renders:

import React, { useCallback, useState } from 'react'; import { View, Button, Text } from 'react-native';

function Counter() { const [count, setCount] = useState(0);

// Bad - Creates new function on every render // const increment = () => setCount(count + 1);

// Good - Memoized callback const increment = useCallback(() => { setCount(c => c + 1); }, []);

return ( <View> <Text>{count}</Text> <Button title="Increment" onPress={increment} /> </View> ); }

Virtualized Lists Only

Use FlatList/SectionList for scrollable content:

// Bad - ScrollView with map (renders all items) <ScrollView> {items.map(item => ( <ItemComponent key={item.id} item={item} /> ))} </ScrollView>

// Good - FlatList (virtualizes items) <FlatList data={items} keyExtractor={item => item.id} renderItem={({ item }) => <ItemComponent item={item} />} />

Avoid Anonymous Functions in Render

// Bad - Creates new function on every render <FlatList data={items} renderItem={({ item }) => ( <TouchableOpacity onPress={() => console.log(item.id)}> <Text>{item.title}</Text> </TouchableOpacity> )} />

// Good - Memoized render function const renderItem = useCallback(({ item }: { item: Item }) => ( <ItemRow item={item} onPress={handleItemPress} /> ), [handleItemPress]);

<FlatList data={items} renderItem={renderItem} />

Optimize Bundle Size

Reduce JavaScript bundle size:

Analyze bundle

npx react-native-bundle-visualizer

Enable Hermes engine (app.json for Expo)

{ "expo": { "jsEngine": "hermes" } }

Enable Hermes (android/app/build.gradle for bare React Native)

project.ext.react = [ enableHermes: true ]

Use ProGuard for Android (android/app/build.gradle)

def enableProguardInReleaseBuilds = true

Code Splitting

Split code for faster initial load:

import React, { lazy, Suspense } from 'react'; import { View, ActivityIndicator } from 'react-native';

// Lazy load heavy components const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() { return ( <Suspense fallback={<ActivityIndicator />}> <HeavyComponent /> </Suspense> ); }

Common Patterns

Debounced Search

import React, { useState, useCallback, useEffect } from 'react'; import { TextInput, FlatList } from 'react-native';

function SearchableList({ data }: { data: Item[] }) { const [query, setQuery] = useState(''); const [debouncedQuery, setDebouncedQuery] = useState('');

// Debounce search useEffect(() => { const timer = setTimeout(() => { setDebouncedQuery(query); }, 300);

return () => clearTimeout(timer);

}, [query]);

const filteredData = useMemo(() => { if (!debouncedQuery) return data; return data.filter(item => item.title.toLowerCase().includes(debouncedQuery.toLowerCase()) ); }, [data, debouncedQuery]);

return ( <> <TextInput value={query} onChangeText={setQuery} placeholder="Search..." /> <FlatList data={filteredData} keyExtractor={item => item.id} renderItem={({ item }) => <ItemComponent item={item} />} /> </> ); }

Optimized Animations

Use react-native-reanimated for smooth animations:

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

function AnimatedButton() { const scale = useSharedValue(1);

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

const handlePressIn = () => { scale.value = withSpring(0.95); };

const handlePressOut = () => { scale.value = withSpring(1); };

return ( <Pressable onPressIn={handlePressIn} onPressOut={handlePressOut}> <Animated.View style={[styles.button, animatedStyle]}> <Text>Press Me</Text> </Animated.View> </Pressable> ); }

Pagination

Implement efficient pagination:

import React, { useState, useCallback } from 'react'; import { FlatList, ActivityIndicator } from 'react-native';

function PaginatedList() { const [data, setData] = useState<Item[]>([]); const [loading, setLoading] = useState(false); const [page, setPage] = useState(1);

const loadMore = useCallback(async () => { if (loading) return;

setLoading(true);
const newData = await fetchData(page);
setData(prev => [...prev, ...newData]);
setPage(p => p + 1);
setLoading(false);

}, [loading, page]);

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

const renderFooter = () => { if (!loading) return null; return <ActivityIndicator style={{ margin: 16 }} />; };

return ( <FlatList data={data} renderItem={renderItem} keyExtractor={item => item.id} onEndReached={loadMore} onEndReachedThreshold={0.5} ListFooterComponent={renderFooter} /> ); }

Memoized Selector

import React, { useMemo } from 'react'; import { View, Text } from 'react-native';

interface User { id: string; name: string; age: number; active: boolean; }

function UserStats({ users }: { users: User[] }) { const stats = useMemo(() => { return { total: users.length, active: users.filter(u => u.active).length, averageAge: users.reduce((sum, u) => sum + u.age, 0) / users.length, }; }, [users]);

return ( <View> <Text>Total: {stats.total}</Text> <Text>Active: {stats.active}</Text> <Text>Average Age: {stats.averageAge.toFixed(1)}</Text> </View> ); }

Image Preloading

import { Image } from 'react-native';

async function preloadImages(imageUrls: string[]) { const promises = imageUrls.map(url => Image.prefetch(url) );

await Promise.all(promises); }

// Usage useEffect(() => { preloadImages([ 'https://example.com/image1.jpg', 'https://example.com/image2.jpg', ]); }, []);

Anti-Patterns

Don't Use console.log in Production

// Bad - Logs slow down production console.log('User data:', user);

// Good - Remove or use DEV if (DEV) { console.log('User data:', user); }

Don't Inline Large Objects

// Bad - Creates new object on every render <Component style={{ width: 100, height: 100, backgroundColor: '#fff' }} config={{ option1: true, option2: false }} />

// Good - Use StyleSheet and constants const styles = StyleSheet.create({ component: { width: 100, height: 100, backgroundColor: '#fff' }, });

const config = { option1: true, option2: false };

<Component style={styles.component} config={config} />

Don't Forget to Clean Up

// Bad - Memory leak useEffect(() => { const timer = setInterval(() => { console.log('Tick'); }, 1000); }, []);

// Good - Clean up useEffect(() => { const timer = setInterval(() => { console.log('Tick'); }, 1000);

return () => clearInterval(timer); }, []);

Don't Use setState in Loops

// Bad - Multiple re-renders items.forEach(item => { setData(prev => [...prev, item]); });

// Good - Single update setData(prev => [...prev, ...items]);

Profiling Tools

React DevTools Profiler

import { Profiler } from 'react';

function onRenderCallback( id: string, phase: 'mount' | 'update', actualDuration: number ) { console.log(${id} (${phase}) took ${actualDuration}ms); }

<Profiler id="MyComponent" onRender={onRenderCallback}> <MyComponent /> </Profiler>

Performance Monitor

import { PerformanceObserver, performance } from 'perf_hooks';

// Enable performance monitor if (DEV) { const observer = new PerformanceObserver((list) => { list.getEntries().forEach((entry) => { console.log(${entry.name}: ${entry.duration}ms); }); });

observer.observe({ entryTypes: ['measure'] }); }

Related Skills

  • react-native-components: Building performant components

  • react-native-navigation: Optimizing navigation performance

  • react-native-native-modules: Native performance optimization

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

android-jetpack-compose

No summary provided by upstream source.

Repository SourceNeeds Review
General

fastapi-async-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

storybook-story-writing

No summary provided by upstream source.

Repository SourceNeeds Review