Use when reviewing React Native or Expo app code for quality issues — architecture, performance, styling, state management, platform differences, accessibility, keyboard handling, navigation, memory leaks, error states, type safety, animation, and security. Use for PRs, feature audits, or full codebase health checks.
How this skill is triggered — by the user, by Claude, or both
Slash command
/react-native-qa-review:react-native-qa-reviewThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Systematic review framework for React Native / Expo applications. Work through each category below. Severity levels: **Critical** (data loss / crash / broken UX), **High** (wrong behavior / user frustration), **Medium** (maintainability / subtle bugs), **Low** (polish / convention).
Systematic review framework for React Native / Expo applications. Work through each category below. Severity levels: Critical (data loss / crash / broken UX), High (wrong behavior / user frustration), Medium (maintainability / subtle bugs), Low (polish / convention).
Red flags: 200+ line render functions, useEffect chains that call other useEffects, god-object context/store slices
StyleSheet.create() used — no inline anonymous style objects (creates new objects every render, bypasses native optimisation)fontFamily explicitly set on all Text and TextInput componentsPlatform.select() or .ios.ts / .android.ts — not Platform.OS === 'ios' ? ... : ... inline in JSXshadowColor/Offset/Opacity/Radius) paired with elevation (Android) on every elevated surfaceSafeAreaView or useSafeAreaInsets used on root screens — no content clipped by notch/home indicatoruseStore((s) => s.field) not useStore((s) => s) (whole-store subscription triggers re-render on any mutation)loading, error, and data fields — all three handled in UIuseState for data that belongs in server cache (React Query / SWR)React.memo on components that receive stable props but re-render from parentuseCallback on functions passed as props to memoized childrenuseMemo for expensive computations, not for cheap ones (over-memoization is also a problem)FlatList / SectionList / FlashList — never ScrollView with .map() for lists of unknown lengthFlatList has keyExtractor returning stable, unique keys (not array index)FlatList has getItemLayout when item height is fixed (enables scroll-to-index and skips measurement)width/height and appropriate resizeMode — no layout thrash from late measurementuseMemo or backgroundKeyboardAvoidingView uses behavior="padding" on iOS and behavior="height" on Androidmultiline TextInput inside ScrollView has nestedScrollEnabled={true} on AndroidTouchableNativeFeedback for Android material ripple where appropriatetry/catch or .catch() on every async function that touches network or storageuseEffect that sets up a subscription, listener, or timer returns a cleanup functionKeyboard, AppState, BackHandler, navigation events) are removed in cleanupsetTimeout / setInterval cleared in cleanupsetState called after unmount (use isMounted ref or cancel async work in cleanup)accessibilityLabel (what it is) and accessibilityHint (what happens when activated)accessibilityRole set on custom interactive components (button, link, image, etc.)Pressable if neededaccessibilityState={{ disabled: true }}TextInput fields have accessibilityLabel matching their visible labelScrollView use KeyboardAvoidingView as outer wrapperScrollView has keyboardShouldPersistTaps="handled"scrollResponderScrollNativeHandleToKeyboard in onFocus handler (or equivalent scrollToPosition)returnKeyType matches context: "next" between fields, "done" or "search" on last fieldonSubmitEditingfocus / blur) used to refresh stale data or cancel operations correctlyany — use unknown with a type guard, or a proper type{} or missing interface)route.params.whatever)any from fetch/axiosuser.profile.name without null checksconsole.log(token), console.log(user))maxLength set on text inputs that feed into API callsuseNativeDriver: true on all Animated calls where possible (transform, opacity — not layout props)useEffect cleanup to prevent updates after unmountLayoutAnimation) called before state change, not after| Mistake | Impact | Fix |
|---|---|---|
useStore((s) => s) | Re-renders on every store change | Select only needed fields |
| Inline style objects | GC pressure, no native caching | StyleSheet.create() |
ScrollView + .map() for lists | Renders all items, OOM on large data | FlatList / FlashList |
No try/catch in async handlers | Silent failures, stuck loading state | Wrap + show error UI |
No useEffect cleanup | Memory leaks, stale callbacks | Always return cleanup fn |
| Hardcoded colors | Design drift, dark mode breakage | Theme constants |
| No keyboard scroll-to-input | Bottom inputs hidden behind keyboard | onFocus scroll handler |
useNativeDriver: false | Janky animation on JS thread | Set to true or use Reanimated |
any on API response | Type errors at runtime | Define response interface |
| Credentials in AsyncStorage | Accessible to other apps on rooted devices | Use SecureStore |
Provides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub mdrooker/claude-skills --plugin react-native-qa-review