From react-native
Builds React Native UI components using core elements like View, Text, Image, ScrollView, FlatList, TextInput, TouchableOpacity, and SafeAreaView. Covers composition, reusability, and best practices.
How this skill is triggered — by the user, by Claude, or both
Slash command
/react-native:react-native-componentsThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use this skill when building user interfaces with React Native's core components and creating custom reusable components.
Use this skill when building user interfaces with React Native's core components and creating custom reusable components.
React Native provides platform-agnostic components that map to native views:
import React from 'react';
import {
View,
Text,
Image,
ScrollView,
TextInput,
TouchableOpacity,
SafeAreaView,
} from 'react-native';
export default function App() {
return (
<SafeAreaView style={{ flex: 1 }}>
<ScrollView>
<View>
<Text>Hello, React Native!</Text>
<Image
source={{ uri: 'https://example.com/image.jpg' }}
style={{ width: 200, height: 200 }}
/>
<TextInput
placeholder="Enter text"
style={{ borderWidth: 1, padding: 10 }}
/>
<TouchableOpacity onPress={() => console.log('Pressed')}>
<Text>Press Me</Text>
</TouchableOpacity>
</View>
</ScrollView>
</SafeAreaView>
);
}
The fundamental building block:
import { View } from 'react-native';
function Container({ children }: { children: React.ReactNode }) {
return (
<View style={{
flex: 1,
padding: 16,
backgroundColor: '#fff',
}}>
{children}
</View>
);
}
All text must be wrapped in <Text>:
import { Text } from 'react-native';
function Heading({ children }: { children: string }) {
return (
<Text style={{
fontSize: 24,
fontWeight: 'bold',
color: '#333',
}}>
{children}
</Text>
);
}
function Body({ children }: { children: string }) {
return (
<Text style={{
fontSize: 16,
lineHeight: 24,
color: '#666',
}}>
{children}
</Text>
);
}
Display images from various sources:
import { Image } from 'react-native';
// Remote image
<Image
source={{ uri: 'https://example.com/image.jpg' }}
style={{ width: 200, height: 200 }}
/>
// Local image
<Image
source={require('./assets/logo.png')}
style={{ width: 100, height: 100 }}
/>
// With resize mode
<Image
source={{ uri: 'https://example.com/image.jpg' }}
style={{ width: 200, height: 200 }}
resizeMode="cover"
/>
Always use SafeAreaView to handle safe areas:
import { SafeAreaView } from 'react-native';
export default function App() {
return (
<SafeAreaView style={{ flex: 1, backgroundColor: '#fff' }}>
{/* Your content */}
</SafeAreaView>
);
}
Use FlatList instead of ScrollView for performance:
import { FlatList, Text, View } from 'react-native';
interface Item {
id: string;
title: string;
}
function ItemList({ items }: { items: Item[] }) {
return (
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ padding: 16 }}>
<Text>{item.title}</Text>
</View>
)}
// Performance optimizations
removeClippedSubviews={true}
maxToRenderPerBatch={10}
updateCellsBatchingPeriod={50}
initialNumToRender={10}
windowSize={10}
/>
);
}
Use appropriate touchables for platform:
import { TouchableOpacity, TouchableHighlight, Pressable } from 'react-native';
// Modern approach - Pressable (recommended)
<Pressable
onPress={() => console.log('Pressed')}
style={({ pressed }) => [
{ padding: 12, backgroundColor: pressed ? '#ddd' : '#fff' }
]}
>
{({ pressed }) => (
<Text style={{ color: pressed ? '#000' : '#333' }}>Press Me</Text>
)}
</Pressable>
// TouchableOpacity - simple fade effect
<TouchableOpacity
onPress={() => console.log('Pressed')}
activeOpacity={0.7}
>
<Text>Press Me</Text>
</TouchableOpacity>
Build complex UIs from simple components:
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 (
<View style={styles.card}>
<View style={styles.header}>
<Text style={styles.title}>{title}</Text>
{subtitle && <Text style={styles.subtitle}>{subtitle}</Text>}
</View>
{children && <View style={styles.content}>{children}</View>}
</View>
);
}
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;
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<void>;
}) {
const [refreshing, setRefreshing] = useState(false);
const handleRefresh = useCallback(async () => {
setRefreshing(true);
await onRefresh();
setRefreshing(false);
}, [onRefresh]);
return (
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ padding: 16 }}>
<Text>{item.title}</Text>
</View>
)}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
}
/>
);
}
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 (
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ padding: 16 }}>
<Text>{item.title}</Text>
</View>
)}
onEndReached={onEndReached}
onEndReachedThreshold={0.5}
ListFooterComponent={
loading ? (
<View style={{ padding: 16 }}>
<ActivityIndicator size="large" />
</View>
) : null
}
/>
);
}
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 (
<Modal
visible={visible}
animationType="slide"
transparent={true}
onRequestClose={onClose}
>
<View style={styles.overlay}>
<View style={styles.modal}>
<View style={styles.header}>
<Text style={styles.title}>{title}</Text>
<TouchableOpacity onPress={onClose}>
<Text style={styles.closeButton}>✕</Text>
</TouchableOpacity>
</View>
<View style={styles.content}>{children}</View>
</View>
</View>
</Modal>
);
}
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;
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 (
<View style={styles.container}>
<Text style={styles.label}>{label}</Text>
<TextInput
{...props}
style={[
styles.input,
isFocused && styles.inputFocused,
error && styles.inputError,
]}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
/>
{error && <Text style={styles.error}>{error}</Text>}
</View>
);
}
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;
// Bad - Nested ScrollViews cause issues
<ScrollView>
<ScrollView>
<Text>Content</Text>
</ScrollView>
</ScrollView>
// Good - Use single ScrollView
<ScrollView>
<View>
<Text>Content</Text>
</View>
</ScrollView>
// Bad - Creates new object on every render
<View style={{ padding: 16, backgroundColor: '#fff' }}>
<Text>Content</Text>
</View>
// Good - Use StyleSheet
const styles = StyleSheet.create({
container: {
padding: 16,
backgroundColor: '#fff',
},
});
<View style={styles.container}>
<Text>Content</Text>
</View>
// Bad - May cause rendering issues
<FlatList
data={items}
renderItem={({ item }) => <Text>{item.title}</Text>}
/>
// Good - Provide unique key
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <Text>{item.title}</Text>}
/>
// Bad - Index as key causes issues with reordering
<FlatList
data={items}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => <Text>{item.title}</Text>}
/>
// Good - Use unique identifier
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <Text>{item.title}</Text>}
/>
npx claudepluginhub thebushidocollective/han --plugin react-nativeProvides React Native patterns for StyleSheet styling, Flexbox layouts, React Navigation, Gesture Handler, and Reanimated 3 animations in cross-platform mobile apps.
Builds React Native and Expo UIs: Expo Router navigation, Flexbox layouts, animations, native controls, modals, gestures, and StyleSheet styling.
Provides React Native best practices for project structure, adaptive styling (NativeWind, StyleSheet), performance-optimized components (memo, Pressable), and FlatList rendering in mobile apps with Expo.