name: rn-best-practices user-invocable: false paths: - "/*.{ts,tsx,js,jsx}" - "/package.json" description: > This skill should be used when writing or reviewing React Native / Expo code — before writing list rendering, animations, data fetching, component APIs, navigation, or image/media UI — and when asked to "review best practices", "check performance", "optimize renders", "review list rendering", "check animation patterns", "review state management", "audit UI", "review composition", "review for production readiness", "check React Native conventions", "performance audit".
React Native Best Practices — Procedural Adapter
This skill is the routing surface between Claude and the vendored
vercel-labs/agent-skills
content under third_party/vercel-labs/agent-skills/. Don't read the 118
rule files upfront. Use the procedures below to look up only the rules
that apply to the code you're about to write or review.
Index
The routing surface is skills/rn-best-practices/rules.index.json. Each entry:
{ "id": "react-native-skills/list-performance-virtualize",
"title": "Use a list virtualizer for any list",
"category": "react-native-skills",
"platform": "RN",
"severity": "CRITICAL" | "HIGH" | "MEDIUM" | "LOW",
"triggers": ["FlatList", "FlashList", "ScrollView"],
"upstream_path": "third_party/vercel-labs/agent-skills/skills/.../list-performance-virtualize.md",
"checkable": true | false,
"checkerRule": "no-touchable-new-code" | null
}
Query with jq or read directly. Filter by platform: "RN" for React Native
work; platform: "both" for composition rules; platform: "web" only for
web React when applicable.
Always check (inline, regardless of category)
These cause runtime crashes. Check on every review pass. Do not delegate.
Falsy-&& rendering crash
{x && <Comp />} where x could be 0, "", or NaN renders the falsy
value as a string in RN. Bad: {count && <Badge />} (renders "0").
Good: {count > 0 && <Badge />} or ternary. Full rule:
third_party/vercel-labs/agent-skills/skills/react-native-skills/rules/rendering-no-falsy-and.md
Bare strings outside <Text>
A string as a direct child of <View> is a runtime crash on RN.
Bad: <View>Hello</View>. Good: <View><Text>Hello</Text></View>.
Full rule: .../rules/rendering-text-in-text-component.md
Components defined inside components
Defining const Child = () => … inside function Parent() creates a new
component type every render. RN remounts it, destroying state, native views,
animations, scroll position, focus. Bad: inline function components inside
parents. Good: define outside, pass data via props. Full rule (web/RN):
.../skills/react-best-practices/rules/rerender-no-inline-components.md
Procedural lookup — query the index BEFORE writing each category
Use these procedures whenever you're about to write or review code in the
listed scope. The procedure tells you which rules.index.json entries to
load; you then read only the matched upstream_path files.
Before writing list rendering (FlatList, FlashList, SectionList, ScrollView+map)
- From
rules.index.json, select entries whereidstarts withreact-native-skills/list-performance-(8 rules). - Read every entry with
severity: CRITICALorHIGH(typically 5-6 rules). - Apply rule recommendations to the code under design/review.
- Cite rule ID in code comments only when the choice is non-obvious
(e.g., why
keyExtractorreturns a non-id field).
Most common applicable rules: list-performance-virtualize,
list-performance-inline-objects, list-performance-callbacks,
list-performance-item-memo.
Before writing animations (Reanimated, react-native-gesture-handler, Animated)
- Select entries where
idstarts withreact-native-skills/animation-(3 rules) ORreact-native-skills/react-compiler-reanimated-. - Read every CRITICAL/HIGH rule.
- Common pitfalls: animating layout props (
width,height,top) instead of GPU-friendlytransform/opacity;useAnimatedReactionfor derived values whereuseDerivedValueis faster.
Before writing data fetching / async flow (useEffect+fetch, React Query, SWR, parallel awaits)
- Select entries where
idstarts withreact-best-practices/async-OR containsquery-cache(custom rule). - Read every CRITICAL/HIGH rule.
- Common pitfalls: sequential
awaiton independent calls (usePromise.all); imperative cache reads (use reactive query hooks).
Before designing component APIs (boolean props, compound vs polymorphic, ref forwarding)
- Select entries where
category: "composition-patterns"(8 rules) ORidends withcompound-components(1 RN rule). - Read every CRITICAL/HIGH rule.
- Common pitfalls: 5+ boolean props on one component (use compound
components);
forwardRefin React 19 code (just receiverefas a prop); render-props wherechildrencomposition would work.
Before writing navigation code (createStackNavigator, Tabs, modal presentation)
- Select entries where
idstarts withreact-native-skills/navigation-OR matchesrn-dev-agent/navigation-*. - Read every CRITICAL/HIGH rule.
- Custom rule alert: on Bridgeless + react-native-screens,
presentation: 'transparentModal'causes routing failures — seereferences/rn-dev-agent/navigation-transparent-modal.md.
Before writing image/media UI (Image, modals, gallery, safe areas, Pressable)
- Select entries where
idstarts withreact-native-skills/ui-(9 rules). - Read every CRITICAL/HIGH rule, plus
ui-pressableeven when LOW (it's a convention enforcement, not a perf issue). - Common pitfalls:
Imagefrom react-native (useexpo-image); JS-rendered bottom sheets (use native modals);Touchable*in new code.
rn-dev-agent custom rules
These four rules are NOT in the upstream Vercel set — they were discovered
through rn-dev-agent story testing on Bridgeless RN 0.76.x. They survive
every upstream sync at references/rn-dev-agent/:
| File | Trigger context |
|---|---|
references/rn-dev-agent/navigation-transparent-modal.md |
Bridgeless + react-native-screens routing |
references/rn-dev-agent/query-cache-reactive.md |
React Query — imperative reads |
references/rn-dev-agent/reanimated-in-lists.md |
Reanimated layout animations inside virtualized lists |
references/rn-dev-agent/theme-memoization-lists.md |
Theme hooks consumed inside renderItem |
These also appear in rules.index.json under category: "rn-dev-agent".
Verification surface (where the deterministic checks live)
A subset of rules has automated grep checking via scripts/check-vercel-rules.mjs
running as a PostToolUse hook on every Edit | MultiEdit | Write against
.tsx/.jsx/.ts/.js files. Currently 3 rules are checkable: true in
the index:
react-native-skills/ui-pressable→no-touchable-new-codereact-native-skills/list-performance-inline-objects→no-inline-renderitem-literalsreact-native-skills/rendering-no-falsy-and→no-falsy-jsx-and
Violations surface as additionalContext injected after the edit — Claude
sees them but they don't block the edit. Block-on-violation behavior lives
in --ci mode (used by the pre-ship-checker integration), not the live
edit loop.
For manual full-project audits: /check-vercel-rules slash command.
Common rationalizations (do not accept these from yourself)
| Excuse | Reality |
|---|---|
| "It's just a 10-item list, I don't need FlashList" | Apps grow. Today's 10 items become tomorrow's 1000. Use FlashList from the start — cost is near-zero, upgrade later is painful. |
| "Inline arrow functions in renderItem are fine for this case" | Every parent re-render produces a new function reference; every list item re-renders. Use useCallback — one extra line. |
"The && pattern is clear enough here" |
items.length && <Content/> renders "0" when items is []. Always use items.length > 0 ? … : null. Crash vector. |
| "I'll handle SafeAreaView later" | Later never comes. Every screen needs safe-area handling from day one — affects hit targets, not just visuals. |
| "User asked for a simple feature — skip the rule review" | Simple features ship. Bad patterns become codebase conventions. Always review at Phase 4 (Architecture) and Phase 6 (Review). |
"I'll use Touchable* everywhere — it's familiar" |
Touchable* is deprecated. Use Pressable — it's the supported API and has built-in pressed/hovered states. |
| "I need to migrate the whole codebase to follow rule X" | Scope discipline. Apply rules to NEW code and code you're already touching. Don't refactor adjacent files. |
Red flags — stop and reconsider
- About to approve code with
&&falsy-value patterns - About to approve a list component that isn't
FlashListorFlatListwith proper memoization - About to approve inline objects or functions inside
renderItem - About to approve a screen without
SafeAreaVieworedgesprop - About to approve
Touchable*in new code (should bePressable) - About to approve
Intl.DateTimeFormatcalled inside render (hoist to module level) - About to approve a component with 5+ boolean props (use compound components)