Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Builds, optimizes, and debugs cross-platform mobile applications with React Native and Expo. Implements navigation hierarchies (tabs, stacks, drawers), configures native modules, optimizes FlatList rendering with memo and useCallback, and handles platform-specific code for iOS and Android. Use when building a React Native or Expo mobile app, setting up navigation, integrating native modules, improving scroll performance, handling SafeArea or keyboard input, or configuring Expo SDK projects.
.claude/skills/jeffallan-react-native-expert/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 26% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 83% | 0% |
| case-04 | ✓→✓ | = Same ✓ | -1% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 34% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 16% | 0% |
Senior mobile engineer building production-ready cross-platform applications with React Native and Expo.
npx expo doctor to verify environment and SDK compatibility; fix any reported issues before proceeding_npx expo start --clear, then restartnpx expo run:iosadb logcat or Gradle output → resolve SDK/NDK version mismatch → rebuild with npx expo run:androidnpx expo install <module> to ensure compatible version, then rebuild native layersLoad detailed guidance based on context:
| Topic | Reference | Load When | |-------|-----------|-----------| | Navigation | references/expo-router.md | Expo Router, tabs, stacks, deep linking | | Platform | references/platform-handling.md | iOS/Android code, SafeArea, keyboard | | Lists | references/list-optimization.md | FlatList, performance, memo | | Storage | references/storage-hooks.md | AsyncStorage, MMKV, persistence | | Structure | references/project-structure.md | Project setup, architecture |
tsximport React, { memo, useCallback } from 'react'; import { FlatList, View, Text, StyleSheet } from 'react-native'; type Item = { id: string; title: string }; const ListItem = memo(({ title, onPress }: { title: string; onPress: () => void }) => ( <View style={styles.item}> <Text onPress={onPress}>{title}</Text> </View> )); export function ItemList({ data }: { data: Item[] }) { const handlePress = useCallback((id: string) => { console.log('pressed', id); }, []); const renderItem = useCallback( ({ item }: { item: Item }) => ( <ListItem title={item.title} onPress={() => handlePress(item.id)} /> ), [handlePress] ); return ( <FlatList data={data} keyExtractor={(item) => item.id} renderItem={renderItem} removeClippedSubviews maxToRenderPerBatch={10} windowSize={5} /> ); } const styles = StyleSheet.create({ item: { padding: 16, borderBottomWidth: StyleSheet.hairlineWidth }, });
tsximport React from 'react'; import { KeyboardAvoidingView, Platform, ScrollView, TextInput, StyleSheet, SafeAreaView, } from 'react-native'; export function LoginForm() { return ( <SafeAreaView style={styles.safe}> <KeyboardAvoidingView style={styles.flex} behavior={Platform.OS === 'ios' ? 'padding' : 'height'} > <ScrollView contentContainerStyle={styles.content} keyboardShouldPersistTaps="handled"> <TextInput style={styles.input} placeholder="Email" autoCapitalize="none" /> <TextInput style={styles.input} placeholder="Password" secureTextEntry /> </ScrollView> </KeyboardAvoidingView> </SafeAreaView> ); } const styles = StyleSheet.create({ safe: { flex: 1 }, flex: { flex: 1 }, content: { padding: 16, gap: 12 }, input: { borderWidth: 1, borderRadius: 8, padding: 12, fontSize: 16 }, });
tsximport { Platform, StyleSheet, View, Text } from 'react-native'; export function StatusChip({ label }: { label: string }) { return ( <View style={styles.chip}> <Text style={styles.label}>{label}</Text> </View> ); } const styles = StyleSheet.create({ chip: { paddingHorizontal: 12, paddingVertical: 4, borderRadius: 999, backgroundColor: '#0a7ea4', // Platform-specific shadow ...Platform.select({ ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, shadowRadius: 4 }, android: { elevation: 3 }, }), }, label: { color: '#fff', fontSize: 13, fontWeight: '600' }, });
When implementing React Native features, deliver:
Platform.select or .ios.tsx / .android.tsx splits as neededReact Native 0.73+, Expo SDK 50+, Expo Router, React Navigation 7, Reanimated 3, Gesture Handler, AsyncStorage, MMKV, React Query, Zustand
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 24,686 | 21,604 | -12% | 1 | 1 | 0% | 5,185 | 5,916 | +14% | 0 | 0 | — |
case-02 | fail→fail | 19,447 | 16,293 | -16% | 1 | 1 | 0% | 4,528 | 5,457 | +21% | 0 | 0 | — |
case-03 | fail→pass | 19,186 | 17,756 | -7% | 1 | 1 | 0% | 4,178 | 5,264 | +26% | 0 | 0 | — |
case-04 | pass→pass | 25,688 | 18,657 | -27% | 1 | 1 | 0% | 5,962 | 5,874 | -1% | 0 | 0 | — |
case-05 | pass→pass | 15,295 | 13,830 | -10% | 1 | 1 | 0% | 3,222 | 4,304 | +34% | 0 | 0 | — |
case-06 | pass→pass | 17,022 | 11,870 | -30% | 1 | 1 | 0% | 3,379 | 3,922 | +16% | 0 | 0 | — |
case-07 | pass→pass | 13,029 | 19,032 | +46% | 1 | 1 | 0% | 2,618 | 5,521 | +111% | 0 | 0 | — |
case-08 | pass→pass | 5,640 | 2,481 | -56% | 1 | 1 | 0% | 494 | 1,877 | +280% | 0 | 0 | — |
case-09 | pass→pass | 5,412 | 4,218 | -22% | 1 | 1 | 0% | 983 | 1,981 | +102% | 0 | 0 | — |
case-10 | pass→pass | 2,528 | 2,752 | +9% | 1 | 1 | 0% | 392 | 1,955 | +399% | 0 | 0 | — |
case-11 | pass→pass | 15,380 | 17,005 | +11% | 1 | 1 | 0% | 3,259 | 5,489 | +68% | 0 | 0 | — |
case-12 | pass→pass | 10,423 | 11,026 | +6% | 1 | 1 | 0% | 2,072 | 3,818 | +84% | 0 | 0 | — |
case-13 | pass→pass | 10,122 | 7,983 | -21% | 1 | 1 | 0% | 2,191 | 3,156 | +44% | 0 | 0 | — |
case-14 | pass→pass | 10,979 | 10,609 | -3% | 1 | 1 | 0% | 2,212 | 3,729 | +69% | 0 | 0 | — |
case-15 | fail→pass | 9,785 | 10,208 | +4% | 1 | 1 | 0% | 2,074 | 3,787 | +83% | 0 | 0 | — |
case-16 | pass→pass | 4,860 | 2,820 | -42% | 1 | 1 | 0% | 871 | 1,948 | +124% | 0 | 0 | — |
case-17 | pass→pass | 6,007 | 2,622 | -56% | 1 | 1 | 0% | 1,045 | 1,960 | +88% | 0 | 0 | — |
case-18 | pass→pass | 11,375 | 11,550 | +2% | 1 | 1 | 0% | 2,187 | 3,949 | +81% | 0 | 0 | — |
case-19 | fail→fail | 14,601 | 13,758 | -6% | 1 | 1 | 0% | 3,156 | 4,468 | +42% | 0 | 0 | — |
case-20 | pass→pass | 12,130 | 12,748 | +5% | 1 | 1 | 0% | 2,451 | 4,021 | +64% | 0 | 0 | — |
case-21 | pass→pass | 12,131 | 8,890 | -27% | 1 | 1 | 0% | 2,414 | 3,335 | +38% | 0 | 0 | — |
case-22 | pass→pass | 13,441 | 10,359 | -23% | 1 | 1 | 0% | 2,617 | 3,625 | +39% | 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. 22 cases were attempted. The headline lift of +9 percentage points is the difference between those two pass rates over the 22 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.