React Native Agent Rules
Project Context
- Use React Native 0.73+ with the New Architecture (Fabric renderer + JSI) enabled by default.
- Prefer Expo SDK for new projects — use bare workflow only when custom native modules require it.
- Use TypeScript with `strict: true` for all source files; configure path aliases in `tsconfig.json` and `babel.config.js`.
- Use `expo-router` for file-based navigation; use `@react-navigation/native-stack` for bare workflow projects.
Code Style & Structure
- Co-locate styles with components using `StyleSheet.create()` at the bottom of each file.
- Keep components under 150 lines. Extract logic into custom hooks (`useAuth`, `useCart`).
- Name platform-specific files with `.ios.ts` and `.android.ts` suffixes for automatic platform splitting.
- Use `Platform.select()` for inline platform differences; use platform files for significant divergence.
- Use named exports for components and hooks; default exports only for screen-level route components.
FlatList & List Performance
- Use `FlatList` for all scrollable lists of more than five items — never `ScrollView` with `map()`.
- Set `keyExtractor` to a stable unique ID string, not the array index.
- Wrap `renderItem` in `useCallback` and list item components in `React.memo()`.
- Use `getItemLayout` when all items have a fixed height to skip measurement overhead.
- Set `removeClippedSubviews={true}` on Android for long lists; keep it off on iOS where it can cause flickers.
- Use `initialNumToRender` (8–12) and `maxToRenderPerBatch` (5–10) to tune rendering on slow devices.
- Use FlashList (`@shopify/flash-list`) over FlatList for lists with many items — always set `estimatedItemSize`.
Navigation
- Use `expo-router` with the `app/` directory for file-based routing — map each screen to a file.
- Pass only serializable data through route params (strings, numbers) — hydrate full objects in the screen.
- Use `<Stack.Screen options={{ ... }} />` inside the screen component for dynamic header configuration.
- Use `router.push`, `router.replace`, and `router.back` from `expo-router` — avoid imperative navigation refs.
- Wrap authenticated routes in a layout component that redirects unauthenticated users at the layout level.
State Management
- Use Zustand or Jotai for global client state — avoid prop drilling beyond two levels.
- Use TanStack Query (`@tanstack/react-query`) for server state — treat it as a cache, not a global store.
- Do not store server-fetched data in Zustand; let TanStack Query own it.
- Use `MMKV` for synchronous persistent storage; use `expo-secure-store` for sensitive values like tokens.
- Initialize TanStack Query's `QueryClient` once at the root and pass a persister for offline support.
Native APIs & Expo Modules
- Use `expo-camera` with the Hooks API (`useCameraPermissions`) rather than the legacy class-based API.
- Use `expo-image` instead of React Native's `<Image>` for memory-efficient caching and blurhash placeholders.
- Use `expo-haptics` for feedback on significant actions — `impactAsync(ImpactFeedbackStyle.Light)`.
- Use `react-native-safe-area-context` with `useSafeAreaInsets()` for manual inset handling.
- Use `expo-notifications` for push notifications; always request permissions before scheduling.
Error Handling
- Use an `ErrorBoundary` component wrapping each major screen to catch render errors gracefully.
- Handle promise rejections in `useEffect` cleanup — unhandled rejections can crash silently on some versions.
- Wrap native module calls in try/catch — native exceptions cross the bridge as JavaScript errors.
- Use `LogBox.ignoreLogs()` only for known third-party warnings, never for application code warnings.
Performance
- Use `InteractionManager.runAfterInteractions()` to defer expensive work until navigation transitions finish.
- Memoize expensive selector computations with `useMemo`; avoid recomputing inside `renderItem`.
- Use `useNativeDriver: true` on all `Animated` animations; switch to Reanimated for gesture-driven animations.
- Use `react-native-reanimated` with `useSharedValue` for 60fps animations running on the UI thread.
- Profile with the React DevTools Profiler and Flipper — address component trees with over 20ms render times.
Testing
- Use Jest with `@testing-library/react-native` for component tests.
- Mock native modules in `jest.setup.ts` using `jest.mock('expo-camera', ...)` patterns.
- Test navigation flows with mocked `expo-router` context using `renderRouter` from `expo-router/testing-library`.
- Write integration tests for critical flows (auth, checkout) using Maestro or Detox.
- Run `npx expo-doctor` in CI to catch native dependency version mismatches before release.