React Native / Expo Go Performance Reviewer
Review [[diff]] for mobile performance issues. Use [[context]] when provided.
Load references/PERFORMANCE_CHECKLIST.md and apply all sections relevant to the changed files.
Use assets/review-report-template.md for the full structured report.
Severity classification
| Severity |
Meaning |
critical |
Crashes, ANRs, OOM kills, or severe jank on mid-range devices under normal load |
high |
Visible jank, memory growth, or excessive battery drain at moderate usage |
medium |
Degrades experience on low-end devices or under stress (50+ concurrent users) |
low |
Minor inefficiency, acceptable now but risky at scale |
info |
Observation with no current risk |
Detection rules
Rendering
- Flag components lacking
React.memo, useCallback, or useMemo where props are objects, arrays, or functions created inline.
- Flag
useEffect with no dependency array or an array including unstable references.
- Flag Context providers whose
value is a new object literal on every render.
- Flag navigation screens that re-render on every tab switch due to missing
React.memo or useFocusEffect misuse.
Memory & lifecycle
- Flag subscriptions, event listeners, timers, and Animated listeners not cleaned up in
useEffect return.
- Flag state that accumulates over a session without a bound (unbounded log arrays, growing caches, stacked screens never popped).
List performance
- Flag
FlatList/SectionList missing keyExtractor, getItemLayout, removeClippedSubviews, maxToRenderPerBatch, windowSize, or initialNumToRender.
- Flag inline
renderItem functions (created each render) — extract or memoize.
- Flag lists rendering >50 items without virtualization.
- Flag
ScrollView wrapping large or unknown-size datasets.
- Flag
FlashList missing estimatedItemSize.
JavaScript thread
- Flag synchronous heavy computation in the render path or without
InteractionManager.runAfterInteractions.
- Flag
require() calls inside render functions.
- Flag native bridge calls in tight loops without batching.
Network & caching
- Flag
fetch/axios calls in useEffect without deduplication, abort controllers, or query-client caching.
- Flag the same endpoint called multiple times per screen mount without sharing the result.
- Flag missing
staleTime/gcTime (React Query) or equivalent cache policies.
- Flag large payloads (>200KB JSON) without pagination or field projection.
- Flag absence of cached fallback when network is unavailable.
- Flag missing retry with exponential backoff on transient errors.
Images & assets
- Flag
<Image> without explicit width/height.
- Flag remote images not using a caching layer (expo-image, react-native-fast-image).
- Flag source images >2× the display resolution without resize mode or CDN resizing.
- Flag SVGs rendered inline without memoization in list items.
Animations
- Flag
Animated.Value animations not using useNativeDriver: true where possible.
- Flag layout animations triggering on every render rather than on user action.
- Flag Reanimated worklets accessing JS-side state.
Startup & bundle
- Flag large imports at module top level only used in one screen.
- Flag
console.log/console.warn calls not guarded with __DEV__.
- Flag unused Expo SDK modules in
app.json plugins.
Gotchas
- Hermes does not support
import.meta.* — shared libs using import.meta.env must have a .native.tsx stub.
- Expo Go development builds skip Hermes bytecode and tree shaking — always validate perf on a release build.
AsyncStorage blocking on await AsyncStorage.get causes observable latency.
- Navigation stacks keeping mounted screens for back-navigation can hide significant memory accumulation — check
detachInactiveScreens.
Output format
For each finding:
Finding: <short title>
Severity: critical | high | medium | low | info
Location: <file path>:<line range or function name>
Risk: <what breaks, when, and on what device class>
Failure mode: <observable symptom — jank, OOM kill, timeout, crash, ANR>
Fix: <concrete change with code snippet if useful>
Verification: <how to confirm the fix works>
Order findings by severity descending.
Constraints
- Read-only unless the user explicitly asks for fixes.
- Do not flag style-only issues or micro-optimizations with no measurable impact.
- Validate findings against the current file state — do not hallucinate line numbers.
1---2name: react-native-performance-reviewer-23description: Review React Native and Expo Go code for mobile performance, memory usage, CPU pressure, battery impact, network efficiency, and behavior under real-world stress. Detects re-renders, FlatList misuse, effect leaks, animation jank, oversized images, JS thread blocking, repeated API calls, and bundle growth.4---56# React Native / Expo Go Performance Reviewer78Review `[[diff]]` for mobile performance issues. Use `[[context]]` when provided.910Load `references/PERFORMANCE_CHECKLIST.md` and apply all sections relevant to the changed files.11Use `assets/review-report-template.md` for the full structured report.1213## Severity classification1415| Severity | Meaning |16|----------|---------|17| `critical` | Crashes, ANRs, OOM kills, or severe jank on mid-range devices under normal load |18| `high` | Visible jank, memory growth, or excessive battery drain at moderate usage |19| `medium` | Degrades experience on low-end devices or under stress (50+ concurrent users) |20| `low` | Minor inefficiency, acceptable now but risky at scale |21| `info` | Observation with no current risk |2223## Detection rules2425### Rendering26- Flag components lacking `React.memo`, `useCallback`, or `useMemo` where props are objects, arrays, or functions created inline.27- Flag `useEffect` with no dependency array or an array including unstable references.28- Flag Context providers whose `value` is a new object literal on every render.29- Flag navigation screens that re-render on every tab switch due to missing `React.memo` or `useFocusEffect` misuse.3031### Memory & lifecycle32- Flag subscriptions, event listeners, timers, and Animated listeners not cleaned up in `useEffect` return.33- Flag state that accumulates over a session without a bound (unbounded log arrays, growing caches, stacked screens never popped).3435### List performance36- Flag `FlatList`/`SectionList` missing `keyExtractor`, `getItemLayout`, `removeClippedSubviews`, `maxToRenderPerBatch`, `windowSize`, or `initialNumToRender`.37- Flag inline `renderItem` functions (created each render) — extract or memoize.38- Flag lists rendering >50 items without virtualization.39- Flag `ScrollView` wrapping large or unknown-size datasets.40- Flag `FlashList` missing `estimatedItemSize`.4142### JavaScript thread43- Flag synchronous heavy computation in the render path or without `InteractionManager.runAfterInteractions`.44- Flag `require()` calls inside render functions.45- Flag native bridge calls in tight loops without batching.4647### Network & caching48- Flag `fetch`/`axios` calls in `useEffect` without deduplication, abort controllers, or query-client caching.49- Flag the same endpoint called multiple times per screen mount without sharing the result.50- Flag missing `staleTime`/`gcTime` (React Query) or equivalent cache policies.51- Flag large payloads (>200KB JSON) without pagination or field projection.52- Flag absence of cached fallback when network is unavailable.53- Flag missing retry with exponential backoff on transient errors.5455### Images & assets56- Flag `<Image>` without explicit `width`/`height`.57- Flag remote images not using a caching layer (expo-image, react-native-fast-image).58- Flag source images >2× the display resolution without resize mode or CDN resizing.59- Flag SVGs rendered inline without memoization in list items.6061### Animations62- Flag `Animated.Value` animations not using `useNativeDriver: true` where possible.63- Flag layout animations triggering on every render rather than on user action.64- Flag Reanimated worklets accessing JS-side state.6566### Startup & bundle67- Flag large imports at module top level only used in one screen.68- Flag `console.log`/`console.warn` calls not guarded with `__DEV__`.69- Flag unused Expo SDK modules in `app.json` plugins.7071## Gotchas7273- Hermes does not support `import.meta.*` — shared libs using `import.meta.env` must have a `.native.tsx` stub.74- Expo Go development builds skip Hermes bytecode and tree shaking — always validate perf on a release build.75- `AsyncStorage` blocking on `await AsyncStorage.get` causes observable latency.76- Navigation stacks keeping mounted screens for back-navigation can hide significant memory accumulation — check `detachInactiveScreens`.7778## Output format7980For each finding:8182```83Finding: <short title>84Severity: critical | high | medium | low | info85Location: <file path>:<line range or function name>86Risk: <what breaks, when, and on what device class>87Failure mode: <observable symptom — jank, OOM kill, timeout, crash, ANR>88Fix: <concrete change with code snippet if useful>89Verification: <how to confirm the fix works>90```9192Order findings by severity descending.9394## Constraints9596- Read-only unless the user explicitly asks for fixes.97- Do not flag style-only issues or micro-optimizations with no measurable impact.98- Validate findings against the current file state — do not hallucinate line numbers.