Lists & Keys

Rendering lists of data is one of the most common things you'll do in React. The pattern is simple: use JavaScript's .map() to transform an array of data into an array of JSX elements.

Basic List Rendering

basic.jsx
function FruitList() {
  const fruits = ['Apple', 'Banana', 'Cherry', 'Date'];

  return (
    <ul>
      {fruits.map((fruit, index) => (
        <li key={index}>{fruit}</li>
      ))}
    </ul>
  );
}

// With objects from an API:
function UserList() {
  const users = [
    { id: 1, name: 'Alice', role: 'admin' },
    { id: 2, name: 'Bob', role: 'student' },
    { id: 3, name: 'Carol', role: 'student' },
  ];

  return (
    <div>
      {users.map(user => (
        <div key={user.id}>
          <strong>{user.name}</strong> — {user.role}
        </div>
      ))}
    </div>
  );
}

Why Keys Matter

React needs a key prop on each list item to track which items changed, were added, or were removed. Without keys, React re-renders the entire list on every change — slow and buggy with form inputs.

keys.jsx
const todos = [
  { id: 'abc1', text: 'Buy groceries', done: false },
  { id: 'abc2', text: 'Write tests', done: true },
  { id: 'abc3', text: 'Ship feature', done: false },
];

// Good: using stable unique IDs
todos.map(todo => <li key={todo.id}>{todo.text}</li>);

// OK: index works only for static, non-reorderable lists
todos.map((todo, i) => <li key={i}>{todo.text}</li>);

// Bad: random keys force React to re-render everything
todos.map(todo => <li key={Math.random()}>{todo.text}</li>);

// Keys must be unique AMONG SIBLINGS, not globally
// Two different lists can have items with the same key
??

Never use the array index as a key if the list can be reordered or filtered. Use a unique ID from your data instead.

Rendering Component Lists

You'll usually render components instead of raw elements:

components.jsx
function ProductCard({ product }) {
  return (
    <div className="card">
      <h3>{product.name}</h3>
      <p>${product.price.toFixed(2)}</p>
      <span className={`badge ${product.inStock ? 'green' : 'red'}`}>
        {product.inStock ? 'In Stock' : 'Out of Stock'}
      </span>
    </div>
  );
}

function ProductGrid({ products }) {
  if (products.length === 0) {
    return <p>No products found.</p>;
  }

  return (
    <div className="grid">
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

Conditional List Items

filter.jsx
import { useState } from 'react';

function FilteredList() {
  const [filter, setFilter] = useState('all');

  const tasks = [
    { id: 1, text: 'Design', status: 'done' },
    { id: 2, text: 'Build', status: 'active' },
    { id: 3, text: 'Test', status: 'active' },
    { id: 4, text: 'Deploy', status: 'pending' },
  ];

  const visible = tasks.filter(t =>
    filter === 'all' ? true : t.status === filter
  );

  return (
    <div>
      <div>
        {['all', 'active', 'done', 'pending'].map(f => (
          <button key={f} onClick={() => setFilter(f)}
            style={{ fontWeight: filter === f ? 'bold' : 'normal' }}>
            {f}
          </button>
        ))}
      </div>
      <ul>
        {visible.map(task => (
          <li key={task.id}>{task.text} ({task.status})</li>
        ))}
      </ul>
      <p>{visible.length} tasks shown</p>
    </div>
  );
}