Rendering Lists in React
Lists are a fundamental part of most applications. React makes it easy to render dynamic lists of data.
Basic List Rendering
Use map() to transform arrays into elements:
jsx
1function NumberList() {
2 const numbers = [1, 2, 3, 4, 5]
3
4 return (
5 <ul>
6 {numbers.map((number) => (
7 <li key={number}>{number}</li>
8 ))}
9 </ul>
10 )
11}The Key Prop
Keys help React identify which items have changed, been added, or removed:
jsx
1// ✅ Good - unique, stable key
2const users = [
3 { id: 1, name: 'Alice' },
4 { id: 2, name: 'Bob' },
5 { id: 3, name: 'Charlie' }
6]
7
8function UserList() {
9 return (
10 <ul>
11 {users.map((user) => (
12 <li key={user.id}>{user.name}</li>
13 ))}
14 </ul>
15 )
16}Key Rules
jsx
1// ✅ Use unique IDs from your data
2{items.map(item => <Item key={item.id} {...item} />)}
3
4// ⚠️ Index as key (only when items won't reorder)
5{items.map((item, index) => <Item key={index} {...item} />)}
6
7// ❌ Don't use random values
8{items.map(item => <Item key={Math.random()} {...item} />)}
9
10// ❌ Don't use non-unique values
11{items.map(item => <Item key={item.name} {...item} />)} // Names might duplicateWhy Keys Matter
jsx
1// Without proper keys, React can't optimize re-renders
2
3// Initial list:
4<li key="1">Apple</li>
5<li key="2">Banana</li>
6<li key="3">Cherry</li>
7
8// After adding "Date" at start:
9<li key="0">Date</li> // New
10<li key="1">Apple</li> // Same key, React knows it's the same item
11<li key="2">Banana</li> // Same key
12<li key="3">Cherry</li> // Same key
13
14// With index as key (bad for insertions):
15<li key="0">Date</li> // Key 0 had Apple, React thinks content changed
16<li key="1">Apple</li> // Key 1 had Banana, React re-renders
17<li key="2">Banana</li> // Key 2 had Cherry, React re-renders
18<li key="3">Cherry</li> // New key, new elementExtracting List Components
jsx
1// List item component
2function TodoItem({ todo, onToggle, onDelete }) {
3 return (
4 <li className={todo.completed ? 'completed' : ''}>
5 <span onClick={() => onToggle(todo.id)}>
6 {todo.text}
7 </span>
8 <button onClick={() => onDelete(todo.id)}>Delete</button>
9 </li>
10 )
11}
12
13// List component
14function TodoList({ todos, onToggle, onDelete }) {
15 return (
16 <ul>
17 {todos.map((todo) => (
18 <TodoItem
19 key={todo.id}
20 todo={todo}
21 onToggle={onToggle}
22 onDelete={onDelete}
23 />
24 ))}
25 </ul>
26 )
27}Empty States
Handle empty lists gracefully:
jsx
1function ProductList({ products }) {
2 if (products.length === 0) {
3 return <p>No products found.</p>
4 }
5
6 return (
7 <ul>
8 {products.map((product) => (
9 <li key={product.id}>{product.name}</li>
10 ))}
11 </ul>
12 )
13}
14
15// Or inline
16function ProductList({ products }) {
17 return (
18 <div>
19 {products.length === 0 ? (
20 <p>No products found.</p>
21 ) : (
22 <ul>
23 {products.map((product) => (
24 <li key={product.id}>{product.name}</li>
25 ))}
26 </ul>
27 )}
28 </div>
29 )
30}Filtering Lists
jsx
1function FilteredList() {
2 const [filter, setFilter] = useState('')
3 const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry']
4
5 const filteredItems = items.filter(item =>
6 item.toLowerCase().includes(filter.toLowerCase())
7 )
8
9 return (
10 <div>
11 <input
12 value={filter}
13 onChange={(e) => setFilter(e.target.value)}
14 placeholder="Filter items..."
15 />
16
17 <ul>
18 {filteredItems.map((item) => (
19 <li key={item}>{item}</li>
20 ))}
21 </ul>
22
23 {filteredItems.length === 0 && (
24 <p>No items match "{filter}"</p>
25 )}
26 </div>
27 )
28}Sorting Lists
jsx
1function SortableList() {
2 const [sortOrder, setSortOrder] = useState('asc')
3 const items = [
4 { id: 1, name: 'Banana', price: 1.5 },
5 { id: 2, name: 'Apple', price: 2.0 },
6 { id: 3, name: 'Cherry', price: 3.5 }
7 ]
8
9 const sortedItems = [...items].sort((a, b) => {
10 if (sortOrder === 'asc') {
11 return a.name.localeCompare(b.name)
12 }
13 return b.name.localeCompare(a.name)
14 })
15
16 return (
17 <div>
18 <button onClick={() => setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc')}>
19 Sort {sortOrder === 'asc' ? '↓' : '↑'}
20 </button>
21
22 <ul>
23 {sortedItems.map((item) => (
24 <li key={item.id}>
25 {item.name} - ${item.price}
26 </li>
27 ))}
28 </ul>
29 </div>
30 )
31}Nested Lists
jsx
1function NestedList() {
2 const categories = [
3 {
4 id: 1,
5 name: 'Fruits',
6 items: ['Apple', 'Banana', 'Cherry']
7 },
8 {
9 id: 2,
10 name: 'Vegetables',
11 items: ['Carrot', 'Broccoli', 'Spinach']
12 }
13 ]
14
15 return (
16 <div>
17 {categories.map((category) => (
18 <div key={category.id}>
19 <h3>{category.name}</h3>
20 <ul>
21 {category.items.map((item) => (
22 <li key={item}>{item}</li>
23 ))}
24 </ul>
25 </div>
26 ))}
27 </div>
28 )
29}Keys Must Be Unique Among Siblings
Keys only need to be unique among siblings, not globally:
jsx
1function App() {
2 return (
3 <div>
4 {/* These can have the same keys */}
5 <ul>
6 {items1.map(item => <li key={item.id}>{item.name}</li>)}
7 </ul>
8
9 <ul>
10 {items2.map(item => <li key={item.id}>{item.name}</li>)}
11 </ul>
12 </div>
13 )
14}Generating Keys
When your data doesn't have IDs:
jsx
1// Option 1: Use a counter (careful with SSR)
2let nextId = 0
3function generateId() {
4 return nextId++
5}
6
7// Option 2: UUID library
8import { v4 as uuidv4 } from 'uuid'
9const newItem = { id: uuidv4(), name: 'New Item' }
10
11// Option 3: Use a hash of the content
12import { hash } from 'some-hash-library'
13const key = hash(item.content)Lists and keys are essential for building dynamic React applications!
