Files
clawmates/skills/mobile/rn-flashlist-perf.md
T
Omar SobhandClaude Opus 4.7 7b23f61632
ci / gates (push) Successful in 4s
ci / frontend (push) Successful in 25s
ci / rust (push) Failing after 3m41s
ci / e2e (push) Skipped
ci / publish (push) Skipped
slice 3.5c: seed 15 built-in skills across the 6 stacks
Hand-authored skill catalog anchored to real 2026-07 versions:
  - Rust 1.97.1 (stable), edition 2024
  - React 19.2.7, Server Components + Actions
  - TailwindCSS 4.3.3 (CSS-first config, Oxide engine)
  - three.js r185 (WebGPURenderer stable, BatchedMesh matured)
  - React Native 0.86 / Expo SDK 54+ (New Architecture default)
  - cargo-nextest 0.9.140, gitleaks 8.20+, cargo-audit 0.21+
  - Postgres 17 (18 in beta, don't rely on)
  - CUDA Blackwell, Metal Apple7+, ROCm CDNA3

Ships 15 skills across the categories:
  foundation/  workspace-repo-commit-protocol
               small-focused-commits
               tdd-red-green-refactor
               code-review-checklist
               int-xx-marker-protocol
               decompose-int-items
  rust/        write-rust-current-edition
               rust-error-handling
               cargo-test-driven-development
               rust-async-tokio-idioms
  backend/     postgres-migrations-forward-only
               postgres-index-selection
               api-pagination-day-1
  frontend/    react-19-server-components
               tailwind-v4-idioms
               component-4-state-model
  mobile/      expo-managed-vs-bare
               rn-flashlist-perf
  gpu/         gpu-coalescing-and-occupancy
               roofline-model
  threejs/     threejs-perf-and-teardown
  security/    cargo-audit-workflow
               secret-scanning-gitleaks

skills_loader.rs walks skills/**/*.md, parses YAML frontmatter
(name, description, when_to_use, tags), upserts via
skills_catalog::upsert_builtin. Idempotent per boot — bumps version
+ appends skill_versions row ONLY when body changes. Deterministic
sha256-derived ids so builtins are stable across boots.

Dockerfile copies skills/ to /etc/clawmates/skills. Server boot
task spawns loader alongside team_template_loader.

Follow-ups (Slice 3.5c continuation, future PRs):
  - 20-30 more skills (duckdb, shadcn composition, a11y, WebGPU
    migration, metal frame capture, rocprof, deep gitea forge
    integration, semgrep rulepacks)
  - Bind skills to team template roles (add [role.skills] refs to
    templates/teams/*.toml + wire template_role_skills population
    in team_template_loader)

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-07-19 13:55:44 -07:00

65 lines
3.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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
<FlashList
data={items}
estimatedItemSize={72}
renderItem={({ item }) => <Row item={item} />}
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 `<GestureHandlerRootView>` 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 `<Image>` — 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".