Org Skills

react-native-development

Write production-quality React Native / Expo code day to day — screens, components, styling with StyleSheet/NativeWind and theme tokens, safe areas and keyboard handling, lists, navigation with Expo Router, forms, images, permissions and Expo SDK modules, platform differences, accessibility, offline and error states, and gestures/animations with Reanimated.

Download .zip Raw Source
When agents use itUse this whenever the user asks to build, implement, refactor or fix a React Native or Expo screen, component, hook, or feature; asks "how do I do X in React Native"; pastes RN code with a bug; or asks about ScrollView vs FlatList, KeyboardAvoidingView, SafeAreaView, Pressable, Platform.select, expo-image, expo-router, permissions, or deep links. Also use it when generating any new React Native code, even small snippets.

Install

Copilot (VS Code, Visual Studio, Copilot CLI and github.com) reads skills from the repository — commit them so the whole team gets them.

npx skills add AGCO-Global/org-skills --skill react-native-development -a github-copilot
# or with the org installer (adds .github/skills/react-native-development):
npx -y github:AGCO-Global/org-skills add skill react-native-development

Uses the open skills CLI. Works with Claude Code, Codex, Cursor, Copilot, Gemini CLI, OpenCode, Windsurf and 60+ others — it asks which agent to install into.

npx skills add AGCO-Global/org-skills --skill react-native-development
# user-level instead of project-level:
npx skills add AGCO-Global/org-skills --skill react-native-development -g

Installs the frontend-react-native-skills plugin, which bundles all Frontend / React Native skills and keeps them updated.

/plugin marketplace add AGCO-Global/org-skills
/plugin install frontend-react-native-skills@org-skills
# or just this skill, in this repository:
npx skills add AGCO-Global/org-skills --skill react-native-development -a claude-code

Use Download .zip above, then upload it under Customize → Skills → Upload skill. Team and Enterprise admins can sync this repo as a plugin marketplace instead.

Installs into .agents/skills/, which Codex reads.

npx skills add AGCO-Global/org-skills --skill react-native-development -a codex

Installs into Cursor's skills folder.

npx skills add AGCO-Global/org-skills --skill react-native-development -a cursor

Installs into Gemini CLI's skills folder.

npx skills add AGCO-Global/org-skills --skill react-native-development -a gemini-cli

Any tool with rules, instructions or custom prompts: use Copy SKILL.md above and paste it in. It is plain Markdown.

Commands use your normal git sign-in to GitHub, so they work while the repository is private. Node.js 20+ required.

Skill contents

React Native Development

Web React habits mostly transfer; the places they don't (no CSS cascade, touch not click, two threads, native permissions, app-store rules) are where bugs live. This skill is the day-to-day implementation guide; architecture and performance have their own skills.

1. Before writing a screen

  1. Which route file renders it? Expo Router file in app/ stays thin; the screen component lives in features/<f>/screens/.
  2. What data, and does it work offline? Query hook with persisted cache; decide what shows with no network.
  3. What platform behaviours differ? Back button (Android), swipe-back (iOS), keyboard, status bar, safe areas, haptics.
  4. What permissions? Request lazily at the moment of use, with a pre-prompt explaining why.

2. Screen template

export function OrderDetailScreen() {
  const { id } = useLocalSearchParams<{ id: string }>();
  const { data, isPending, isError, refetch } = useOrder(id);
  const insets = useSafeAreaInsets();

  if (isPending) return <ScreenSkeleton />;
  if (isError) return <ErrorState onRetry={refetch} />;

  return (
    <View style={[styles.root, { paddingBottom: insets.bottom }]}>
      <Stack.Screen options={{ title: data.number }} />
      <ScrollView contentContainerStyle={styles.content} keyboardShouldPersistTaps="handled">
        <OrderSummary order={data} />
      </ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  root: { flex: 1, backgroundColor: tokens.color.bg },
  content: { padding: tokens.space[4], gap: tokens.space[3] },
});

Conventions:

  • StyleSheet.create at module bottom, styles named by role (root, content, title), tokens from shared/theme; no magic numbers.
  • flex: 1 on the root, gap for spacing between children (supported) instead of margin hacks.
  • Safe areas via useSafeAreaInsets() (react-native-safe-area-context) applied as padding — not SafeAreaView inside tabs/stacks, which double-pads.
  • Header/title configured with <Stack.Screen options> in the screen, not the layout, when it depends on data.
  • Typed params: useLocalSearchParams<{ id: string }>(); validate/parse (Number(id)) before use.

3. Components and touch

  • Pressable for anything tappable (never TouchableOpacity in new code): android_ripple, style={({ pressed }) => …}, hitSlop to reach 44 pt, accessibilityRole="button", accessibilityLabel, accessibilityState={{ disabled }}.
  • Text must wrap all strings — a bare string inside View crashes. Set allowFontScaling policy and maxFontSizeMultiplier on constrained labels; test at 200 % dynamic type.
  • No nested ScrollViews in the same direction; lists inside a screen are FlashList with ListHeaderComponent, not ScrollView + map.
  • Platform differences: Platform.select({ ios, android }) inline for small values; Component.ios.tsx / .android.tsx for divergent implementations; Platform.OS === 'web' when targeting web too.
  • Shadows: shadow* props on iOS, elevation on Android; wrap in a helper (shadow(2)) so you never write both by hand.
  • Borders need explicit borderWidth; overflow: 'hidden' clips borderRadius on Android for images/children.
  • Fonts loaded once with useFonts behind the splash; reference by fontFamily string from tokens.

4. Lists

  • FlashList: data, renderItem, keyExtractor, estimatedItemSize, ItemSeparatorComponent, ListEmptyComponent, onEndReached + onEndReachedThreshold={0.5} for infinite scroll, refreshing/onRefresh for pull-to-refresh.
  • Row components: memoised, receive primitives or stable objects, no inline closures capturing the row — pass id and let the row call the callback.
  • getItemType when rows differ in layout (headers vs items).
  • Sticky headers via stickyHeaderIndices; sections via SectionList or FlashList with typed items.

5. Navigation (Expo Router)

  • Push vs replace vs navigate: router.push adds to stack, router.replace swaps (after login/complete), router.navigate goes to existing if present.
  • <Link href="/orders/[id]" asChild><Pressable>…</Pressable></Link> for tappable navigation with prefetch; href typed with typed routes.
  • Modals: route group (modals) with presentation: 'modal'; dismiss via router.back() / router.dismiss().
  • Auth gating in the root layout via <Redirect> based on session; never conditionally render different Stacks.
  • Deep links: every screen reachable by URL; parse and validate params; handle missing entity with a designed 404 screen.
  • Android back: useFocusEffect + BackHandler only for genuine intercepts (unsaved changes); otherwise let the router handle it.
  • Tabs: <Tabs.Screen options={{ tabBarIcon, title, href: null /* hide */ }}>; badge via tabBarBadge.

6. Keyboard and forms

  • KeyboardAvoidingView with behavior={Platform.OS === 'ios' ? 'padding' : undefined} at the screen level, or react-native-keyboard-controller for reliable cross-platform behaviour.
  • ScrollView keyboardShouldPersistTaps="handled" so taps on buttons work while the keyboard is up; keyboardDismissMode="on-drag".
  • Inputs: returnKeyType, onSubmitEditing to focus the next field (ref.current?.focus()), autoComplete/textContentType for autofill, keyboardType, autoCapitalize="none" for emails.
  • React Hook Form + Zod with Controller (RN inputs are controlled); errors shown under the field with accessibilityLiveRegion="polite".
  • Disable submit while pending; dismiss keyboard (Keyboard.dismiss()) before showing success.

7. Images and assets

  • expo-image everywhere: source={{ uri }}, placeholder={blurhash}, contentFit="cover", transition={200}, cachePolicy, explicit style dimensions.
  • Local assets via require('@/assets/x.png') centralised in assets/index.ts; use @2x/@3x variants or SVG via react-native-svg.
  • Icons: @expo/vector-icons or a custom SVG icon component with accessibilityLabel when meaningful.

8. Device APIs, permissions, native modules

  • Use the Expo SDK module (expo-camera, expo-location, expo-notifications, expo-haptics, expo-clipboard, expo-secure-store, expo-file-system) before any community lib.
  • Permissions: check status → show your own explanation → request → handle denied/blocked with a "Open settings" (Linking.openSettings()) path. Add NSxxxUsageDescription / Android permissions via config plugin in app.json, never by editing native folders.
  • Wrap module calls in try/catch; simulators lack camera/biometrics — guard with isAvailableAsync().
  • Haptics on meaningful confirmations only; respect system reduce-motion via AccessibilityInfo.isReduceMotionEnabled.

9. Animations and gestures (implementation)

  • Reanimated: useSharedValue, useAnimatedStyle, withTiming/withSpring; entering/exiting layout animations (FadeIn, Layout); never Animated from core in new code.
  • Gesture Handler: Gesture.Pan()/Tap()/Pinch() + GestureDetector; runOnJS for callbacks into React; wrap app root in GestureHandlerRootView.
  • Bottom sheets: @gorhom/bottom-sheet; don't hand-roll.
  • Keep animation logic in a hook (useSwipeToDismiss) so components stay declarative.

10. Storage, network, offline (implementation)

  • Query hooks per feature; show cached data instantly with a subtle "updated x ago"/offline banner from useNetInfo.
  • MMKV for settings/cache flags (storage.getString), SecureStore for tokens (await SecureStore.getItemAsync).
  • Mutations while offline: queue via TanStack Query's mutation cache with networkMode: 'offlineFirst', or show a clear "will sync when online" state — never silently fail.
  • All fetches through the shared apiClient (base URL, auth header refresh, timeout via AbortController).

11. Accessibility defaults

  • Every interactive element: accessibilityRole, accessibilityLabel (when text isn't the label), accessibilityState, accessibilityHint for non-obvious results.
  • Group related text with accessible on the container so the screen reader reads once.
  • Announce results: AccessibilityInfo.announceForAccessibility() after async actions.
  • Test with VoiceOver/TalkBack for one key flow per feature; check dynamic type at max.

12. Definition of done for a screen

  • Loading, empty, error (with retry), offline states rendered
  • Safe areas and keyboard verified on iOS notch device and small Android
  • Typed route params, deep-linkable, back behaviour correct on Android
  • Touch targets ≥ 44 pt, labels set, screen reader pass
  • Tokens only — no hardcoded colours/sizes; dark mode checked
  • Jest test for logic + snapshot-free component test (see testing skill); works in release build

Anti-patterns to fix on sight

  • ScrollView + .map() for data lists; nested same-direction scroll views.
  • TouchableOpacity/TouchableHighlight in new code; <View onTouchEnd> as a button.
  • Strings outside <Text>; hardcoded font sizes without scaling policy.
  • Dimensions.get('window') at module scope; SafeAreaView inside nested navigators.
  • AsyncStorage for tokens; tokens in Zustand persisted to MMKV.
  • Editing ios//android/ folders in a managed Expo app instead of a config plugin.
  • Requesting all permissions at launch.
  • Animated.Value + setState per frame; PanResponder.
  • Business logic in app/ route files or _layout.tsx.