Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Master React Native styling, navigation, and Reanimated animations for cross-platform mobile development. Use when building React Native apps, implementing navigation patterns, or creating performant animations.
.claude/skills/dicklesworthstone-react-native-design/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-09 | ✗→✓ | ▲ Improved | 127% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 153% | 0% |
| case-23 | ✓→✓ | = Same ✓ | 184% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 158% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 86% | 0% |
Master React Native styling patterns, React Navigation, and Reanimated 3 to build performant, cross-platform mobile applications with native-quality user experiences.
Basic StyleSheet:
typescriptimport { StyleSheet, View, Text } from 'react-native'; const styles = StyleSheet.create({ container: { flex: 1, padding: 16, backgroundColor: '#ffffff', }, title: { fontSize: 24, fontWeight: '600', color: '#1a1a1a', marginBottom: 8, }, subtitle: { fontSize: 16, color: '#666666', lineHeight: 24, }, }); function Card() { return ( <View style={styles.container}> <Text style={styles.title}>Title</Text> <Text style={styles.subtitle}>Subtitle text</Text> </View> ); }
Dynamic Styles:
typescriptinterface CardProps { variant: 'primary' | 'secondary'; disabled?: boolean; } function Card({ variant, disabled }: CardProps) { return ( <View style={[ styles.card, variant === 'primary' ? styles.primary : styles.secondary, disabled && styles.disabled, ]}> <Text style={styles.text}>Content</Text> </View> ); } const styles = StyleSheet.create({ card: { padding: 16, borderRadius: 12, }, primary: { backgroundColor: '#6366f1', }, secondary: { backgroundColor: '#f3f4f6', borderWidth: 1, borderColor: '#e5e7eb', }, disabled: { opacity: 0.5, }, text: { fontSize: 16, }, });
Row and Column Layouts:
typescriptconst styles = StyleSheet.create({ // Vertical stack (column) column: { flexDirection: "column", gap: 12, }, // Horizontal stack (row) row: { flexDirection: "row", alignItems: "center", gap: 8, }, // Space between items spaceBetween: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", }, // Centered content centered: { flex: 1, justifyContent: "center", alignItems: "center", }, // Fill remaining space fill: { flex: 1, }, });
Stack Navigator:
typescriptimport { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; type RootStackParamList = { Home: undefined; Detail: { itemId: string }; Settings: undefined; }; const Stack = createNativeStackNavigator<RootStackParamList>(); function AppNavigator() { return ( <NavigationContainer> <Stack.Navigator initialRouteName="Home" screenOptions={{ headerStyle: { backgroundColor: '#6366f1' }, headerTintColor: '#ffffff', headerTitleStyle: { fontWeight: '600' }, }} > <Stack.Screen name="Home" component={HomeScreen} options={{ title: 'Home' }} /> <Stack.Screen name="Detail" component={DetailScreen} options={({ route }) => ({ title: `Item ${route.params.itemId}`, })} /> <Stack.Screen name="Settings" component={SettingsScreen} /> </Stack.Navigator> </NavigationContainer> ); }
Tab Navigator:
typescriptimport { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { Ionicons } from '@expo/vector-icons'; type TabParamList = { Home: undefined; Search: undefined; Profile: undefined; }; const Tab = createBottomTabNavigator<TabParamList>(); function TabNavigator() { return ( <Tab.Navigator screenOptions={({ route }) => ({ tabBarIcon: ({ focused, color, size }) => { const icons: Record<string, keyof typeof Ionicons.glyphMap> = { Home: focused ? 'home' : 'home-outline', Search: focused ? 'search' : 'search-outline', Profile: focused ? 'person' : 'person-outline', }; return <Ionicons name={icons[route.name]} size={size} color={color} />; }, tabBarActiveTintColor: '#6366f1', tabBarInactiveTintColor: '#9ca3af', })} > <Tab.Screen name="Home" component={HomeScreen} /> <Tab.Screen name="Search" component={SearchScreen} /> <Tab.Screen name="Profile" component={ProfileScreen} /> </Tab.Navigator> ); }
Animated Values:
typescriptimport Animated, { useSharedValue, useAnimatedStyle, withSpring, withTiming, } from 'react-native-reanimated'; function AnimatedBox() { const scale = useSharedValue(1); const opacity = useSharedValue(1); const animatedStyle = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }], opacity: opacity.value, })); const handlePress = () => { scale.value = withSpring(1.2, {}, () => { scale.value = withSpring(1); }); }; return ( <Pressable onPress={handlePress}> <Animated.View style={[styles.box, animatedStyle]} /> </Pressable> ); }
Gesture Handler Integration:
typescriptimport { Gesture, GestureDetector } from 'react-native-gesture-handler'; import Animated, { useSharedValue, useAnimatedStyle, withSpring, } from 'react-native-reanimated'; function DraggableCard() { const translateX = useSharedValue(0); const translateY = useSharedValue(0); const gesture = Gesture.Pan() .onUpdate((event) => { translateX.value = event.translationX; translateY.value = event.translationY; }) .onEnd(() => { translateX.value = withSpring(0); translateY.value = withSpring(0); }); const animatedStyle = useAnimatedStyle(() => ({ transform: [ { translateX: translateX.value }, { translateY: translateY.value }, ], })); return ( <GestureDetector gesture={gesture}> <Animated.View style={[styles.card, animatedStyle]}> <Text>Drag me!</Text> </Animated.View> </GestureDetector> ); }
typescriptimport { Platform, StyleSheet } from "react-native"; const styles = StyleSheet.create({ container: { padding: 16, ...Platform.select({ ios: { shadowColor: "#000", shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, }, android: { elevation: 4, }, }), }, text: { fontFamily: Platform.OS === "ios" ? "SF Pro Text" : "Roboto", fontSize: 16, }, }); // Platform-specific components import { Platform } from "react-native"; const StatusBarHeight = Platform.OS === "ios" ? 44 : 0;
typescriptimport React from 'react'; import { View, Text, StyleSheet, Pressable, Image, } from 'react-native'; import Animated, { useSharedValue, useAnimatedStyle, withSpring, } from 'react-native-reanimated'; interface ItemCardProps { title: string; subtitle: string; imageUrl: string; onPress: () => void; } const AnimatedPressable = Animated.createAnimatedComponent(Pressable); export function ItemCard({ title, subtitle, imageUrl, onPress }: ItemCardProps) { const scale = useSharedValue(1); const animatedStyle = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }], })); return ( <AnimatedPressable style={[styles.card, animatedStyle]} onPress={onPress} onPressIn={() => { scale.value = withSpring(0.97); }} onPressOut={() => { scale.value = withSpring(1); }} > <Image source={{ uri: imageUrl }} style={styles.image} /> <View style={styles.content}> <Text style={styles.title} numberOfLines={1}> {title} </Text> <Text style={styles.subtitle} numberOfLines={2}> {subtitle} </Text> </View> </AnimatedPressable> ); } const styles = StyleSheet.create({ card: { backgroundColor: '#ffffff', borderRadius: 16, overflow: 'hidden', shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 8, elevation: 4, }, image: { width: '100%', height: 160, backgroundColor: '#f3f4f6', }, content: { padding: 16, gap: 4, }, title: { fontSize: 18, fontWeight: '600', color: '#1f2937', }, subtitle: { fontSize: 14, color: '#6b7280', lineHeight: 20, }, });
React.memo and useCallback to prevent unnecessary rerendersSafeAreaView or useSafeAreaInsetsGestureDetector and use simultaneousHandlersParamList types for all navigatorsrunOnUI workletsexpo-font or react-native-asset for custom fonts| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-23 | pass→pass | 9,148 | 9,847 | +8% | 1 | 1 | 0% | 1,623 | 4,611 | +184% | 0 | 0 | — |
case-01 | pass→pass | 10,196 | 28,312 | +178% | 1 | 1 | 0% | 1,930 | 4,984 | +158% | 0 | 0 | — |
case-02 | pass→pass | 12,470 | 9,025 | -28% | 1 | 1 | 0% | 2,629 | 4,877 | +86% | 0 | 0 | — |
case-03 | pass→pass | 8,998 | 7,057 | -22% | 1 | 1 | 0% | 1,509 | 4,040 | +168% | 0 | 0 | — |
case-04 | pass→pass | 15,343 | 11,514 | -25% | 1 | 1 | 0% | 2,407 | 5,335 | +122% | 0 | 0 | — |
case-05 | pass→pass | 12,444 | 12,744 | +2% | 1 | 1 | 0% | 2,612 | 5,744 | +120% | 0 | 0 | — |
case-06 | pass→pass | 15,079 | 14,382 | -5% | 1 | 1 | 0% | 2,954 | 5,285 | +79% | 0 | 0 | — |
case-07 | pass→pass | 7,283 | 7,242 | -1% | 1 | 1 | 0% | 1,525 | 4,336 | +184% | 0 | 0 | — |
case-16 | pass→pass | 5,919 | 4,316 | -27% | 1 | 1 | 0% | 1,141 | 3,845 | +237% | 0 | 0 | — |
case-08 | pass→pass | 14,044 | 9,034 | -36% | 1 | 1 | 0% | 2,533 | 4,433 | +75% | 0 | 0 | — |
case-09 | fail→pass | 20,099 | 23,510 | +17% | 1 | 1 | 0% | 3,274 | 7,434 | +127% | 0 | 0 | — |
case-10 | pass→pass | 19,351 | 15,647 | -19% | 1 | 1 | 0% | 3,884 | 6,309 | +62% | 0 | 0 | — |
case-11 | pass→pass | 3,418 | 3,637 | +6% | 1 | 1 | 0% | 641 | 3,645 | +469% | 0 | 0 | — |
case-17 | fail→pass | 9,086 | 5,003 | -45% | 1 | 1 | 0% | 1,509 | 3,822 | +153% | 0 | 0 | — |
case-12 | pass→pass | 5,839 | 6,152 | +5% | 1 | 1 | 0% | 1,222 | 4,170 | +241% | 0 | 0 | — |
case-13 | pass→pass | 8,556 | 7,263 | -15% | 1 | 1 | 0% | 1,756 | 4,498 | +156% | 0 | 0 | — |
case-14 | pass→pass | 19,423 | 23,167 | +19% | 1 | 1 | 0% | 2,743 | 6,474 | +136% | 0 | 0 | — |
case-15 | pass→pass | 14,521 | 16,797 | +16% | 1 | 1 | 0% | 3,120 | 6,719 | +115% | 0 | 0 | — |
case-22 | pass→pass | 13,630 | 17,316 | +27% | 1 | 1 | 0% | 2,517 | 6,179 | +145% | 0 | 0 | — |
case-18 | pass→pass | 14,415 | 15,720 | +9% | 1 | 1 | 0% | 2,663 | 6,301 | +137% | 0 | 0 | — |
case-19 | pass→pass | 5,875 | 8,036 | +37% | 1 | 1 | 0% | 1,112 | 4,330 | +289% | 0 | 0 | — |
case-20 | pass→pass | 14,036 | 20,307 | +45% | 1 | 1 | 0% | 3,060 | 6,636 | +117% | 0 | 0 | — |
case-21 | pass→pass | 11,416 | 8,642 | -24% | 1 | 1 | 0% | 1,936 | 4,743 | +145% | 0 | 0 | — |
case-24 | pass→pass | 11,170 | 10,611 | -5% | 1 | 1 | 0% | 2,166 | 5,236 | +142% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 24 cases were attempted. The headline lift of +8 percentage points is the difference between those two pass rates over the 24 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.