React Keys and Lists
Render lists efficiently with the key prop, understand reconciliation, and avoid common list rendering bugs.
TL;DR
- 01Always provide a key prop when rendering lists of items.
- 02Use stable, unique identifiers — not array indices.
- 03Keys help React identify which items changed for efficient updates.
Tips
- 01Always use a unique identifier from your data as the key, preferably an ID from a database or UUID library.
Warnings
- 01Using array indices as keys causes bugs when lists are reordered, filtered, or items are added/removed dynamically.
Why Keys Matter
- Keys help React identify which items have changed, been added, or removed.
const items = [ { id: 1, name: "Alice" }, { id: 2, name: "Bob" } ]; {items.map(item => ( <div key={item.id}>{item.name}</div> ))} - Without keys, React assumes items are in the same position.
- This causes state to persist incorrectly between items.
// Without keys: input state follows the position, not the person {people.map((person, index) => ( <div key={index}> <input defaultValue={person.name} /> </div> ))} - Keys ensure component state stays with the correct item.
Choosing Good Keys
- Use stable, unique identifiers from your data.
// Good: unique ID {items.map(item => ( <div key={item.id}>{item.name}</div> ))} - Avoid array indices as keys, especially for dynamic lists.
// Bad: index changes when items are reordered {items.map((item, index) => ( <div key={index}>{item.name}</div> ))} - Use IDs from a database or UUID library for reliable keys.
import { v4 as uuidv4 } from "uuid"; const newItem = { id: uuidv4(), name: "Charlie" }; - Keys don't need to be globally unique, just unique within the list.
Common Mistakes
- Don't use array indices when the list can be reordered.
// Problem: if items are sorted, indices change const sorted = items.sort((a, b) => a.name.localeCompare(b.name)); sorted.map((item, i) => <Item key={i} />); // Bad! - Don't generate keys on the fly with random values.
// Bad: new key generated on every render items.map(item => ( <div key={Math.random()}>{item.name}</div> )) - Don't use complex objects as keys — use simple strings or numbers.
// Bad: object reference changes {items.map(item => ( <div key={item}>{item.name}</div> ))} - Always provide a key prop when rendering with map.
List Rendering Patterns
- Render a simple list with stable keys.
export default function UserList({ users }) { return ( <ul> {users.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> ); } - Handle empty lists gracefully.
if (users.length === 0) { return <p>No users found</p>; } return ( <ul> {users.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> ); - Filter and sort before rendering, not in the map.
const active = users.filter(u => u.isActive); const sorted = active.sort((a, b) => a.name.localeCompare(b.name)); return sorted.map(user => ( <UserItem key={user.id} user={user} /> ));
Performance Considerations
- Keys allow React to reuse DOM elements and preserve component state.
// With proper keys, React updates only changed items {items.map(item => ( <Item key={item.id} data={item} /> ))} - Avoid creating new components inside map — extract to separate component.
// Bad: defines component inside map items.map(item => { const Component = createComponent(item); return <Component key={item.id} />; }) // Good: use a wrapper component items.map(item => ( <ItemWrapper key={item.id} item={item} /> )) - Use React.memo on list items to avoid unnecessary re-renders.
const ListItem = React.memo(({ item }) => ( <div>{item.name}</div> )); items.map(item => ( <ListItem key={item.id} item={item} /> ))
FAQ
React uses keys to track which items changed, were added, or removed between renders. Without keys, React falls back to index-based diffing, which can cause incorrect component reuse and state bugs when the list changes.
No — keys generated at render time (like Math.random()) change every render, forcing React to unmount and remount every list item each time. This destroys component state and tanks performance.
An index-based key shifts when items are reordered or deleted, causing React to match the wrong component to the wrong data. A stable ID (like a database primary key) always points to the same item regardless of position.
No — keys only need to be unique among siblings in the same list. The same key value can appear in a completely different list without conflict.
Use a library like uuid or nanoid to assign a stable ID when the item is first created (e.g., on user input), and store that ID with the data. Never generate the key inline during render — generate it once and persist it.