# React Native List Performance

> Optimizes React Native FlatList and SectionList performance through windowing, item memoization, getItemLayout, and efficient rendering strategies for smooth scrolling.

- Skill: `paulpas/react-native-list-performance` (Agent Skill)
- Install (CLI): `npx skillmds@latest add paulpas/react-native-list-performance`
- Raw SKILL.md: https://api.skillmd.com/api/skills/paulpas/react-native-list-performance/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- License: MIT
- Author: paulpas (https://skillmd.com/u/paulpas)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/paulpas/react-native-list-performance

---


# React Native List Performance

Optimizes FlatList and SectionList rendering through windowed virtualization, fixed-size item layout measurements, memoized item components, and tuned batch rendering parameters. Achieves smooth 60fps scrolling even with thousands of items.

## TL;DR Checklist

- [ ] Use FlatList (not ScrollView with map) for any list over 20 items
- [ ] Extract list item components and wrap with React.memo
- [ ] Implement getItemLayout for fixed-height items
- [ ] Set windowSize (default 21) based on scroll smoothness vs. memory tradeoff
- [ ] Use stable, unique keyExtractor (never index)
- [ ] Profile scroll performance with built-in FPS monitor
- [ ] Consider FlashList for lists exceeding 500 items

---

## When to Use

Use this skill when:

- Rendering any dynamic list exceeding 20 items
- Building infinite-scroll feeds, chat threads, or data tables
- Diagnosing scroll jank or dropped frames in long lists
- Migrating from ScrollView + map() to virtualized lists
- Displaying variable-height items (comments, cards with dynamic content)
- Optimizing initial render time for lists with hundreds of items

---

## When NOT to Use

Avoid this skill for:

- Lists under 20 items with simple content — ScrollView is simpler and sufficient
- Static content that never changes — consider ScrollView for clarity
- Short forms or settings screens — FlatList overhead isn't justified
- Navigation-based lists (use React Navigation's built-in optimized rendering)

---

## Core Workflow

1. **Choose the Right List Component** — FlatList for homogeneous items, SectionList for sectioned data, FlashList for extreme performance needs.

2. **Extract and Memoize Item Components** — Move renderItem logic to a standalone component wrapped with React.memo.

   **Checkpoint:** Verify the item component doesn't create new objects/arrays on each render.

3. **Configure getItemLayout** — Provide fixed item heights to skip measurement.

   **Checkpoint:** Confirm all items are the same height before using getItemLayout.

4. **Tune Virtualization Parameters** — Adjust windowSize, maxToRenderPerBatch, initialNumToRender, and removeClippedSubviews.

5. **Profile** — Enable the FPS monitor and verify 60fps during rapid scrolling.

---

## Implementation Patterns

### Pattern 1: Proper FlatList Setup (BAD vs. GOOD)

```tsx
// ❌ BAD: Inline renderItem, no memo, index as key, no getItemLayout
<FlatList
  data={items}
  renderItem={({ item }) => (
    <View style={styles.item}>
      <Text>{item.name}</Text>
      <Text>{item.description}</Text>
    </View>
  )}
  keyExtractor={(_, index) => String(index)}
/>
```

```tsx
// ✅ GOOD: Memoized item component, stable key, getItemLayout
interface ListItemData {
  id: string;
  name: string;
  description: string;
}

const ListItem = React.memo(function ListItem({ item }: { item: ListItemData }) {
  return (
    <View style={styles.item}>
      <Text style={styles.title}>{item.name}</Text>
      <Text style={styles.subtitle}>{item.description}</Text>
    </View>
  );
});

const ITEM_HEIGHT = 72; // Fixed height for all items

function OptimizedList({ items }: { items: ListItemData[] }) {
  const renderItem = useCallback(
    ({ item }: { item: ListItemData }) => <ListItem item={item} />,
    []
  );

  return (
    <FlatList
      data={items}
      renderItem={renderItem}
      keyExtractor={(item) => item.id}
      getItemLayout={(_, index) => ({
        length: ITEM_HEIGHT,
        offset: ITEM_HEIGHT * index,
        index,
      })}
      windowSize={15}
      maxToRenderPerBatch={10}
      initialNumToRender={12}
      removeClippedSubviews={true}
    />
  );
}
```

### Pattern 2: Avoiding Inline Object Creations in renderItem

```tsx
// ❌ BAD: New style object created on every renderItem call
<FlatList
  data={items}
  renderItem={({ item }) => (
    <View style={{ padding: 16, backgroundColor: item.isActive ? '#eef' : '#fff' }}>
      <Text>{item.name}</Text>
    </View>
  )}
/>
```

```tsx
// ✅ GOOD: Styles extracted, conditional styling via computed value
const styles = StyleSheet.create({
  item: { padding: 16 },
  itemActive: { backgroundColor: '#eef' },
  itemInactive: { backgroundColor: '#fff' },
});

const ListItem = React.memo(function ListItem({ item }: { item: ItemData }) {
  return (
    <View style={[styles.item, item.isActive ? styles.itemActive : styles.itemInactive]}>
      <Text>{item.name}</Text>
    </View>
  );
});
```

### Pattern 3: Variable Height with getItemType

```tsx
// ✅ GOOD: Dynamic item types for variable-height items
type ListItemType = 'header' | 'message' | 'divider';

interface ChatItem {
  id: string;
  type: ListItemType;
  text?: string;
  sender?: string;
}

const ITEM_HEIGHTS: Record<ListItemType, number> = {
  header: 44,
  message: 80,
  divider: 24,
};

function ChatList({ messages }: { messages: ChatItem[] }) {
  const getItemLayout = useCallback(
    (data: ArrayLike<ChatItem> | null, index: number) => {
      const item = data?.[index];
      const length = ITEM_HEIGHTS[item?.type ?? 'message'];
      const offset = calculateOffset(data, index, ITEM_HEIGHTS);
      return { length, offset, index };
    },
    []
  );

  return (
    <FlatList
      data={messages}
      getItemLayout={getItemLayout}
      keyExtractor={(item) => item.id}
      renderItem={renderChatItem}
    />
  );
}

// Helper: precompute offsets for variable heights
function calculateOffset(
  data: ArrayLike<ChatItem> | null,
  index: number,
  heights: Record<string, number>
): number {
  let offset = 0;
  for (let i = 0; i < index; i++) {
    offset += heights[data?.[i]?.type ?? 'message'];
  }
  return offset;
}
```

### Pattern 4: FlashList for Extreme Performance

```tsx
// ✅ GOOD: FlashList for lists over 500 items — better memory and scroll performance
import { FlashList } from '@shopify/flash-list';

function LargeFeed({ posts }: { posts: Post[] }) {
  return (
    <FlashList
      data={posts}
      renderItem={({ item }) => <PostCard post={item} />}
      keyExtractor={(item) => item.id}
      estimatedItemSize={120}
      getItemType={(item) => item.type} // Enables type-based reuse
    />
  );
}
```

---

## Constraints

### MUST DO
- Use FlatList or FlashList for any dynamic list with more than 20 items
- Implement `getItemLayout` when all items share the same height
- Wrap list item components with `React.memo` to prevent unnecessary re-renders
- Use `useCallback` for renderItem to avoid re-creating the function on every render
- Use stable, unique keys in `keyExtractor` — never use the index
- Extract styles and static objects outside the component to avoid re-creation
- Set `removeClippedSubviews={true}` for lists with complex item layouts

### MUST NOT DO
- Use ScrollView with `map()` for lists over 20 items — lacks windowing
- Create new objects, arrays, or functions inside `renderItem`
- Use the array index as the list item key
- Set `windowSize` unnecessarily high (max 31) — consumes more memory
- Inline styles or objects in renderItem — they're recreated on every render
- Forget to memoize item components — they re-render on every parent update

---

## Related Skills

| Skill | Purpose |
|---|---|
| `react-native-animation` | Animate list items with Reanimated for smooth transitions |
| `react-native-navigation` | Navigate to list item detail screens with shared element transitions |
| `react-native-ui-patterns` | Responsive list layouts with Flexbox and useWindowDimensions |

---

## Live References

> Authoritative documentation links for this skill's domain. The model follows markdown links at load time to resolve external references and inline content.

- [React Native FlatList Documentation](https://reactnative.dev/docs/flatlist)
- [React Native SectionList Documentation](https://reactnative.dev/docs/sectionlist)
- [React Native Performance Overview](https://reactnative.dev/docs/performance)
- [Shopify FlashList Documentation](https://shopify.github.io/flash-list/)
- [React Native FPS Monitor](https://reactnative.dev/docs/debugging#fps-monitor)
- [Optimizing FlatList Configuration](https://reactnative.dev/docs/optimizing-flatlist-configuration)
- [React.memo API Reference](https://react.dev/reference/react/memo)

