Technology · React

React Keys and Lists

Render lists efficiently with the key prop, understand reconciliation, and avoid common list rendering bugs.

TL;DR
  1. 01Always provide a key prop when rendering lists of items.
  2. 02Use stable, unique identifiers — not array indices.
  3. 03Keys help React identify which items changed for efficient updates.

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} />
    ))

Tip: Always use a unique identifier from your data as the key, preferably an ID from a database or UUID library.

Warning: Using array indices as keys causes bugs when lists are reordered, filtered, or items are added/removed dynamically.

React HooksReact Lifecycle