--- name: react-key-prop description: Guides proper usage of the key prop in React lists. Use this skill when rendering lists, mapping arrays to components, or troubleshooting list-related state bugs. --- # React: Key Prop Best Practices ## Core Principle **Use stable, unique IDs from your data. Never use array index for dynamic lists.** The `key` prop provides stable identity to list elements during React's reconciliation process. ## When to Use What ### Use Data IDs (Preferred) Always use unique, stable identifiers directly from your data: ```jsx // ✅ Correct {todos.map((todo) => (
  • {todo.text}
  • ))} ``` Ideal keys are: - **Unique** - No two items share the same key - **Stable** - Never changes during component lifetime - **Predictable** - Directly tied to the data item ### Generate IDs on Data Load When data lacks IDs, create them **once** when receiving data: ```jsx import { nanoid } from 'nanoid'; useEffect(() => { fetch('/api/items') .then(res => res.json()) .then(data => { const itemsWithIds = data.map(item => ({ ...item, _tempId: nanoid() // Stable ID generated once })); setItems(itemsWithIds); }); }, []); ``` ### When Index Is Acceptable (Rare) Index as key is acceptable ONLY when ALL conditions are met: - List is absolutely static - Items never added/removed (except at the end) - Order never changes - Items have no internal state ## Anti-Patterns to Avoid ### Never Generate Keys During Render ```jsx // ❌ WRONG: Creates new key every render {items.map(item => (
  • {item.name}
  • ))} ``` This forces React to destroy and recreate all components on every render. ### Don't Use Index for Dynamic Lists ```jsx // ❌ WRONG for dynamic lists {items.map((item, index) => (
  • {item.name}
  • ))} ``` Index fails when: - Items are added/removed from beginning or middle - List order changes (sorting, filtering) - Items have internal state (like form inputs) **The bug:** Index represents position, not data identity. When positions change but indexes stay the same, React incorrectly "mutates" existing components instead of creating new ones, causing state mismatch. ### Don't Use `useId()` for List Keys React's `useId()` hook is for accessibility (linking labels to inputs), not for generating list keys. ## Quick Reference ### DO - Always use `key` when rendering lists - Prefer unique, stable `id` from your data - Generate IDs once at data load time (`nanoid`/`uuid`) ### DON'T - Never generate `key` during render (`Math.random()`, `Date.now()`) - Avoid `index` as `key` for dynamic lists - Don't use `useId()` for list keys ## References - [React Docs - Rendering Lists](https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key) - [nanoid - Tiny ID generator](https://github.com/ai-cookie/nanoid)