list-performance
<!-- Loaded by react-native-engineer when task involves lists, FlatList, FlashList, LegendList, virtualization, renderItem, scroll performance -->
$ npx -y skills add notque/vexjoy-agent --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
<!-- Loaded by react-native-engineer when task involves lists, FlatList, FlashList, LegendList, virtualization, renderItem, scroll performance -->
Agent definition
list-performance.mdList Performance Reference
<!-- Loaded by react-native-engineer when task involves lists, FlatList, FlashList, LegendList, virtualization, renderItem, scroll performance -->
Use a Virtualizer for Any Scrollable List
**Impact:** CRITICAL — only visible items rendered, reduces memory and mount time
**Instead of:**
<ScrollView>
{items.map((item) => <ItemCard key={item.id} item={item} />)}
</ScrollView>**Use:**
import { LegendList } from '@legendapp/list'
function Feed({ items }: { items: Item[] }) {
return (
<LegendList
data={items}
renderItem={({ item }) => <ItemCard item={item} />}
keyExtractor={(item) => item.id}
estimatedItemSize={80}
/>
)
}---
Keep Stable Object References — Transform Inside Items
**Impact:** CRITICAL — virtualization skips re-renders only when references are stable
Mapping/filtering before passing to list creates new references every render, forcing full re-renders on every state update.
**Instead of:**
const domains = tlds.map((tld) => ({
domain: `${keyword}.${tld.name}`,
price: tld.price,
}))
return <LegendList data={domains} ... />**Use:**
<LegendList
data={tlds} // stable references
renderItem={({ item }) => <DomainItem tld={item} />}
/>
function DomainItem({ tld }: { tld: Tld }) {
const domain = useKeywordStore((s) => s.keyword + '.' + tld.name)
return <Text>{domain}</Text>
}New sorted arrays are fine if inner object references stay the same: `tlds.toSorted(...)`.
---
Pass Primitives and Stable References to List Items
**Impact:** HIGH — prevents re-renders of memoized items
Inline objects/styles in `renderItem` create new references every render, breaking `memo()`.
**Instead of:**
renderItem={({ item }) => (
<UserRow
user={{ id: item.id, name: item.name }}
style={{ backgroundColor: item.isActive ? 'green' : 'gray' }}
/>
)}**Use:**
renderItem={({ item }) => (
<UserRow id={item.id} name={item.name} isActive={item.isActive} />
)}
// Or hoist static styles to module scope
const activeStyle = { backgroundColor: 'green' }
const inactiveStyle = { backgroundColor: 'gray' }---
Pass Primitives for Effective Memoization
**Impact:** HIGH — enables shallow comparison in memo()
**Instead of:**
const UserRow = memo(function UserRow({ user }: { user: User }) {
return <Text>{user.name}</Text>
})
renderItem={({ item }) => <UserRow user={item} />}**Use:**
const UserRow = memo(function UserRow({ id, name }: { id: string; name: string }) {
return <Text>{name}</Text>
})
renderItem={({ item }) => <UserRow id={item.id} name={item.name} />}---
Hoist Callbacks to the Root — Items Call with ID
**Impact:** HIGH — single stable callback vs new closure per item
**Instead of:**
renderItem={({ item }) => {
const onPress = () => handlePress(item.id)
return <Item item={item} onPress={onPress} />
}}**Use:**
const onPress = useCallback((id: string) => handlePress(id), [handlePress])
renderItem={({ item }) => <Item item={item} onPress={onPress} />}---
Keep List Items Lightweight
**Impact:** HIGH — reduces per-item render time during scroll
**Instead of:**
function ProductRow({ id }: { id: string }) {
const { data: product } = useQuery(['product', id], () => fetchProduct(id))
const theme = useContext(ThemeContext)
const cart = useContext(CartContext)
const recs = useMemo(() => computeRecommendations(product), [product])
return <View>{/* ... */}</View>
}**Use:**
function ProductList() {
const { data: products } = useQuery(['products'], fetchProducts)
return (
<LegendList
data={products}
renderItem={({ item }) => (
<ProductRow name={item.name} price={item.price} imageUrl={item.image} />
)}
/>
)
}
function ProductRow({ name, price, imageUrl }: Props) {
const inCart = useCartStore((s) => s.items.has(id))
return <View>{/* ... */}</View>
}No queries in items, no expensive computations, prefer Zustand selectors over Context.
---
Use Item Types for Heterogeneous Lists
**Impact:** HIGH — efficient recycling, prevents layout thrashing
<LegendList
data={items}
keyExtractor={(item) => item.id}
getItemType={(item) => item.type}
getEstimatedItemSize={(index, item, itemType) => {
switch (itemType) {
case 'header': return 48
case 'message': return 72
case 'image': return 300
default: return 72
}
}}
renderItem={({ item }) => {
switch (item.type) {
case 'header': return <SectionHeader title={item.title} />
case 'message': return <MessageRow text={item.text} />
case 'image': return <ImageRow url={item.url} />
}
}}
recycleItems
/>---
Use Compressed, Appropriately-Sized Images in Lists
**Impact:** HIGH — reduces memory; thumbnails load at display dimensions
**Instead of:**
<Image source={{ uri: product.imageUrl }} style={{ width: 100, height: 100 }} />**Use:**
import { Image } from 'expo-image'
function ProductItem({ product }: { product: Product }) {
const thumbnailUrl = `${product.imageUrl}?w=200&h=200&fit=cover` // 2x retina
return (
<Image
source={{ uri: thumbnailUrl }}
style={{ width: 100, height: 100 }}
contentFit="cover"
recyclingKey={product.id}
/>
)
}Use `expo-image` for list images — memory-efficient caching and `recyclingKey` for recycled cells.
Read more
List Performance Reference
<!-- Loaded by react-native-engineer when task involves lists, FlatList, FlashList, LegendList, virtualization, renderItem, scroll performance -->
Use a Virtualizer for Any Scrollable List
**Impact:** CRITICAL — only visible items rendered, reduces memory and mount time
**Instead of:**
<ScrollView>
{items.map((item) => <ItemCard key={item.id} item={item} />)}
</ScrollView>**Use:**
import { LegendList } from '@legendapp/list'
function Feed({ items }: { items: Item[] }) {
return (
<LegendList
data={items}
renderItem={({ item }) => <ItemCard item={item} />}
keyExtractor={(item) => item.id}
estimatedItemSize={80}
/>
)
}---
Keep Stable Object References — Transform Inside Items
**Impact:** CRITICAL — virtualization skips re-renders only when references are stable
Mapping/filtering before passing to list creates new references every render, forcing full re-renders on every state update.
**Instead of:**
const domains = tlds.map((tld) => ({
domain: `${keyword}.${tld.name}`,
price: tld.price,
}))
return <LegendList data={domains} ... />**Use:**
<LegendList
data={tlds} // stable references
renderItem={({ item }) => <DomainItem tld={item} />}
/>
function DomainItem({ tld }: { tld: Tld }) {
const domain = useKeywordStore((s) => s.keyword + '.' + tld.name)
return <Text>{domain}</Text>
}New sorted arrays are fine if inner object references stay the same: `tlds.toSorted(...)`.
---
Pass Primitives and Stable References to List Items
**Impact:** HIGH — prevents re-renders of memoized items
Inline objects/styles in `renderItem` create new references every render, breaking `memo()`.
**Instead of:**
renderItem={({ item }) => (
<UserRow
user={{ id: item.id, name: item.name }}
style={{ backgroundColor: item.isActive ? 'green' : 'gray' }}
/>
)}**Use:**
renderItem={({ item }) => (
<UserRow id={item.id} name={item.name} isActive={item.isActive} />
)}
// Or hoist static styles to module scope
const activeStyle = { backgroundColor: 'green' }
const inactiveStyle = { backgroundColor: 'gray' }---
Pass Primitives for Effective Memoization
**Impact:** HIGH — enables shallow comparison in memo()
**Instead of:**
const UserRow = memo(function UserRow({ user }: { user: User }) {
return <Text>{user.name}</Text>
})
renderItem={({ item }) => <UserRow user={item} />}**Use:**
const UserRow = memo(function UserRow({ id, name }: { id: string; name: string }) {
return <Text>{name}</Text>
})
renderItem={({ item }) => <UserRow id={item.id} name={item.name} />}---
Hoist Callbacks to the Root — Items Call with ID
**Impact:** HIGH — single stable callback vs new closure per item
**Instead of:**
renderItem={({ item }) => {
const onPress = () => handlePress(item.id)
return <Item item={item} onPress={onPress} />
}}**Use:**
const onPress = useCallback((id: string) => handlePress(id), [handlePress])
renderItem={({ item }) => <Item item={item} onPress={onPress} />}---
Keep List Items Lightweight
**Impact:** HIGH — reduces per-item render time during scroll
**Instead of:**
function ProductRow({ id }: { id: string }) {
const { data: product } = useQuery(['product', id], () => fetchProduct(id))
const theme = useContext(ThemeContext)
const cart = useContext(CartContext)
const recs = useMemo(() => computeRecommendations(product), [product])
return <View>{/* ... */}</View>
}**Use:**
function ProductList() {
const { data: products } = useQuery(['products'], fetchProducts)
return (
<LegendList
data={products}
renderItem={({ item }) => (
<ProductRow name={item.name} price={item.price} imageUrl={item.image} />
)}
/>
)
}
function ProductRow({ name, price, imageUrl }: Props) {
const inCart = useCartStore((s) => s.items.has(id))
return <View>{/* ... */}</View>
}No queries in items, no expensive computations, prefer Zustand selectors over Context.
---
Use Item Types for Heterogeneous Lists
**Impact:** HIGH — efficient recycling, prevents layout thrashing
<LegendList
data={items}
keyExtractor={(item) => item.id}
getItemType={(item) => item.type}
getEstimatedItemSize={(index, item, itemType) => {
switch (itemType) {
case 'header': return 48
case 'message': return 72
case 'image': return 300
default: return 72
}
}}
renderItem={({ item }) => {
switch (item.type) {
case 'header': return <SectionHeader title={item.title} />
case 'message': return <MessageRow text={item.text} />
case 'image': return <ImageRow url={item.url} />
}
}}
recycleItems
/>---
Use Compressed, Appropriately-Sized Images in Lists
**Impact:** HIGH — reduces memory; thumbnails load at display dimensions
**Instead of:**
<Image source={{ uri: product.imageUrl }} style={{ width: 100, height: 100 }} />**Use:**
import { Image } from 'expo-image'
function ProductItem({ product }: { product: Product }) {
const thumbnailUrl = `${product.imageUrl}?w=200&h=200&fit=cover` // 2x retina
return (
<Image
source={{ uri: thumbnailUrl }}
style={{ width: 100, height: 100 }}
contentFit="cover"
recyclingKey={product.id}
/>
)
}Use `expo-image` for list images — memory-efficient caching and `recyclingKey` for recycled cells.
Essays and writing behind this toolkit live at vexjoy.com. AI agents skip steps. "Looks correct" replaces running tests. "Trivial change" replaces verification.
Repo: notque/vexjoy-agent
Other agents on vexjoy-agent.
- ansible-automation-engineer
Ansible automation: playbooks, roles, collections, Molecule testing, Vault security.
Open agent - modules
**Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes **Version range**: ansible-core 2.14+ / Ansible Collections (community.general 7.0+) **Generated**: 2026-04-04 — verify against current Ansible
Open agent - testing
**Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns **Version range**: Molecule 6.0+ / ansible-lint 6.0+ / ansible-core 2.14+ **Generated**: 2026-04-04 — verify against current Molecule and ansible-lint documentation
Open agent - base-instructions
Universal operational rules injected by /do at agent dispatch. Domain-specific rules live in each agent's .md file.
Open agent - communication-patterns
**Scope**: Failure modes in agent output style — over-reporting, self-congratulation, verbose narration, and hedging. Covers what to detect and how to fix each. **Version range**: all versions **Generated**: 2026-05-11
Open agent - combat-effects-upgrade
Zero-dependency combat visual upgrades: CSS particle replacement, Framer Motion combat juice, CSS 3D card transforms.
Open agent

