--- name: rn-flashlist-perf description: React Native list performance — FlashList (Shopify) over FlatList for anything > 20 rows. Reanimated 3 for animations. New Architecture-aware. when_to_use: You're rendering scrollable lists or animations in RN 0.86+. Mobile team pins. tags: [mobile, react-native, performance] --- # RN performance essentials ## Lists - **FlashList** (`@shopify/flash-list` — 1.7+ for RN 0.86 New Architecture support) over `FlatList` for any list of 20+ rows. Recycles views instead of unmounting. - **Estimated item size** is REQUIRED for FlashList perf. Measure a typical row height, plug in. ```tsx } keyExtractor={(i) => i.id} /> ``` - **`getItemType`** for heterogeneous lists — FlashList recycles per type, so a section-header + row list gets 2 recycled pools. - **`overrideItemLayout`** when items have known-in-advance heights that vary — skips measurement pass. ## Animations - **Reanimated 3.x** for anything animating > 3× per frame (60+/s). Runs on the UI thread — no JS bridge bounce. - **Never** `Animated` (the legacy API) for gesture-driven interactions. It goes through the JS bridge and jitters under load. - **Worklets** for anything that reads a shared value + computes. Marked `"worklet"` at the top of the fn. ```tsx const scale = useSharedValue(1); const style = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }] })); // Update from JS: scale.value = withSpring(1.1); ``` ## Gesture handling - **`react-native-gesture-handler` v2.x** — required, not optional. Wrap `` at the app root. - **`Gesture.Pan()`** etc. over `PanResponder`. Composes with Reanimated worklets natively. ## Image handling - **`expo-image`** for any image where you'd have reached for `` — automatic caching, disk + memory tiers, format-optimal decoding. - **Never** load a >1024px image directly. Resize server-side or via `expo-image-manipulator`. - **Prefer WebP or AVIF** for static assets — smaller than PNG at same quality. ## Bridge crossings to avoid - **`console.log` in prod builds** — goes through the bridge, has real cost. Strip with `babel-plugin-transform-remove-console`. - **Anonymous fns in `renderItem`** — trigger reconciliation every render. Extract outside or `useCallback`. - **Inline styles that create new objects every render** — same problem. Extract with `StyleSheet.create` or memoize. ## New Architecture caveats (default in SDK 54+) - **Fabric renderer** — layout is synchronous. `onLayout` fires reliably. - **Turbo Modules** — native calls are typed + can be sync where safe. If you own a native module and haven't migrated, do it (Codegen handles the JSI wrapper). - **Some libraries still lag New Arch** — check the Fabric compat table; those still bounce through the bridge until they update. ## Anti-patterns - **`ScrollView` with 200 hard-mounted children.** ScrollView renders all children up front — use FlashList always. - **`setInterval` for animation.** Reanimated `withRepeat` runs on UI thread; setInterval jitters. - **`InteractionManager.runAfterInteractions`** as a general delay tool. Its actual semantics are subtle; use `setTimeout(0)` if you just want "next tick".