--- name: react-native-components user-invocable: false description: Use when building React Native UI components with core components, custom components, and component patterns. Covers View, Text, Image, ScrollView, FlatList, and component composition. allowed-tools: - Read - Write - Edit - Bash - Grep - Glob --- # React Native Components Use this skill when building user interfaces with React Native's core components and creating custom reusable components. ## Key Concepts ### Core Components React Native provides platform-agnostic components that map to native views: ```tsx import React from 'react'; import { View, Text, Image, ScrollView, TextInput, TouchableOpacity, SafeAreaView, } from 'react-native'; export default function App() { return ( Hello, React Native! console.log('Pressed')}> Press Me ); } ``` ### View Component The fundamental building block: ```tsx import { View } from 'react-native'; function Container({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` ### Text Component All text must be wrapped in ``: ```tsx import { Text } from 'react-native'; function Heading({ children }: { children: string }) { return ( {children} ); } function Body({ children }: { children: string }) { return ( {children} ); } ``` ### Image Component Display images from various sources: ```tsx import { Image } from 'react-native'; // Remote image // Local image // With resize mode ``` ## Best Practices ### Use SafeAreaView for iOS Notch Always use SafeAreaView to handle safe areas: ```tsx import { SafeAreaView } from 'react-native'; export default function App() { return ( {/* Your content */} ); } ``` ### FlatList for Long Lists Use FlatList instead of ScrollView for performance: ```tsx import { FlatList, Text, View } from 'react-native'; interface Item { id: string; title: string; } function ItemList({ items }: { items: Item[] }) { return ( item.id} renderItem={({ item }) => ( {item.title} )} // Performance optimizations removeClippedSubviews={true} maxToRenderPerBatch={10} updateCellsBatchingPeriod={50} initialNumToRender={10} windowSize={10} /> ); } ``` ### Touchable Components Use appropriate touchables for platform: ```tsx import { TouchableOpacity, TouchableHighlight, Pressable } from 'react-native'; // Modern approach - Pressable (recommended) console.log('Pressed')} style={({ pressed }) => [ { padding: 12, backgroundColor: pressed ? '#ddd' : '#fff' } ]} > {({ pressed }) => ( Press Me )} // TouchableOpacity - simple fade effect console.log('Pressed')} activeOpacity={0.7} > Press Me ``` ### Component Composition Build complex UIs from simple components: ```tsx import React from 'react'; import { View, Text, StyleSheet } from 'react-native'; interface CardProps { title: string; subtitle?: string; children?: React.ReactNode; } function Card({ title, subtitle, children }: CardProps) { return ( {title} {subtitle && {subtitle}} {children && {children}} ); } const styles = StyleSheet.create({ card: { backgroundColor: '#fff', borderRadius: 8, padding: 16, marginVertical: 8, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 3, }, header: { marginBottom: 12, }, title: { fontSize: 18, fontWeight: 'bold', color: '#333', }, subtitle: { fontSize: 14, color: '#666', marginTop: 4, }, content: { marginTop: 8, }, }); export default Card; ``` ## Common Patterns ### List with Pull-to-Refresh ```tsx import React, { useState, useCallback } from 'react'; import { FlatList, RefreshControl, Text, View } from 'react-native'; interface Item { id: string; title: string; } function RefreshableList({ items, onRefresh }: { items: Item[]; onRefresh: () => Promise; }) { const [refreshing, setRefreshing] = useState(false); const handleRefresh = useCallback(async () => { setRefreshing(true); await onRefresh(); setRefreshing(false); }, [onRefresh]); return ( item.id} renderItem={({ item }) => ( {item.title} )} refreshControl={ } /> ); } ``` ### Infinite Scroll List ```tsx import React from 'react'; import { FlatList, ActivityIndicator, View } from 'react-native'; interface Item { id: string; title: string; } function InfiniteList({ items, loading, onEndReached }: { items: Item[]; loading: boolean; onEndReached: () => void; }) { return ( item.id} renderItem={({ item }) => ( {item.title} )} onEndReached={onEndReached} onEndReachedThreshold={0.5} ListFooterComponent={ loading ? ( ) : null } /> ); } ``` ### Modal Component ```tsx import React from 'react'; import { Modal, View, Text, TouchableOpacity, StyleSheet, } from 'react-native'; interface CustomModalProps { visible: boolean; title: string; children: React.ReactNode; onClose: () => void; } function CustomModal({ visible, title, children, onClose }: CustomModalProps) { return ( {title} {children} ); } const styles = StyleSheet.create({ overlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.5)', justifyContent: 'center', alignItems: 'center', }, modal: { width: '80%', backgroundColor: '#fff', borderRadius: 12, padding: 20, }, header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16, }, title: { fontSize: 20, fontWeight: 'bold', }, closeButton: { fontSize: 24, color: '#666', }, content: { marginTop: 8, }, }); export default CustomModal; ``` ### Form Input Component ```tsx import React, { useState } from 'react'; import { View, TextInput, Text, StyleSheet, TextInputProps, } from 'react-native'; interface FormInputProps extends TextInputProps { label: string; error?: string; } function FormInput({ label, error, ...props }: FormInputProps) { const [isFocused, setIsFocused] = useState(false); return ( {label} setIsFocused(true)} onBlur={() => setIsFocused(false)} /> {error && {error}} ); } const styles = StyleSheet.create({ container: { marginVertical: 8, }, label: { fontSize: 14, fontWeight: '600', marginBottom: 4, color: '#333', }, input: { borderWidth: 1, borderColor: '#ddd', borderRadius: 8, padding: 12, fontSize: 16, }, inputFocused: { borderColor: '#007AFF', }, inputError: { borderColor: '#FF3B30', }, error: { color: '#FF3B30', fontSize: 12, marginTop: 4, }, }); export default FormInput; ``` ## Anti-Patterns ### Don't Nest ScrollViews ```tsx // Bad - Nested ScrollViews cause issues Content // Good - Use single ScrollView Content ``` ### Don't Use Inline Styles for Static Values ```tsx // Bad - Creates new object on every render Content // Good - Use StyleSheet const styles = StyleSheet.create({ container: { padding: 16, backgroundColor: '#fff', }, }); Content ``` ### Don't Forget to Set keyExtractor ```tsx // Bad - May cause rendering issues {item.title}} /> // Good - Provide unique key item.id} renderItem={({ item }) => {item.title}} /> ``` ### Don't Use Index as Key ```tsx // Bad - Index as key causes issues with reordering index.toString()} renderItem={({ item }) => {item.title}} /> // Good - Use unique identifier item.id} renderItem={({ item }) => {item.title}} /> ``` ## Related Skills - **react-native-styling**: Styling components with StyleSheet - **react-native-navigation**: Navigation between screens - **react-native-performance**: Optimizing component performance