React Lists & Keys
Render dynamic lists with map(), keys, filter, sort, and pagination
map()
Keys
filter()
Pagination
1. Introduction: Rendering Lists in React
In React, rendering lists of data (like arrays of products, users, or messages) is a fundamental task. You'll often need to take an array of data and convert it into an array of JSX elements.
Analogy: Think of map() as a printing press. You give it an array of data (the "original documents"), and it produces an array of components (the "printed copies") — one for each item.
The core principle: In React, you don't write loops like for or while directly inside JSX. Instead, you use JavaScript array methods (especially map(), filter(), reduce()) to transform your data into JSX.
JSX // Data (array)
const users = ["Alice", "Bob", "Charlie"];
// React way — map() inside JSX
<ul>
{users.map(user => <li key={user}>{user}</li>)}
</ul>
// Wrong way — for loop inside JSX (doesn't work)
<ul>
{for (let i = 0; i < users.length; i++) {
<li>{users[i]}</li>
}}
</ul>
2. The map() Function: Transforming Arrays into JSX
2.1 Basic map() Syntax
JavaScript const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2); // [2, 4, 6, 8, 10]
const names = ["Alice", "Bob", "Charlie"];
const nameElements = names.map(name => <li>{name}</li>);
2.2 Rendering Arrays of Primitives
JSX function SimpleList() {
const fruits = ["Apple", "Banana", "Orange", "Mango", "Grapes"];
return (
<ul>
{fruits.map(fruit => (
<li key={fruit}>{fruit}</li>
))}
</ul>
);
}
2.3 Rendering Arrays of Objects
JSX function ProductList() {
const products = [
{ id: 1, name: "Laptop", price: 999, inStock: true },
{ id: 2, name: "Mouse", price: 29, inStock: true },
{ id: 3, name: "Keyboard", price: 79, inStock: false }
];
return (
<div className="product-grid">
{products.map(product => (
<div key={product.id} className="product-card">
<h3>{product.name}</h3>
<p>Price: ${product.price}</p>
<p>{product.inStock ? "In Stock" : "Out of Stock"}</p>
</div>
))}
</div>
);
}
2.4 map() with Index Parameter
The map() function passes a second argument: the index of the current item.
JSX function NumberedList() {
const tasks = ["Learn React", "Build a project", "Apply for jobs"];
return (
<ol>
{tasks.map((task, index) => (
<li key={index}>{index + 1}. {task}</li>
))}
</ol>
);
}
Note: Using index as a key is generally not recommended for dynamic lists (explained in section 3.6).
3. Keys in React: Why They Matter
3.1 What are Keys?
Keys are special string attributes that help React identify which items in a list have changed, been added, or been removed. The key is a special prop — it is not accessible inside the child component.
Analogy: Keys are like fingerprints or student ID numbers. Even if two students have the same name, their ID numbers uniquely identify them.
JSX {items.map(item => (
<ListItem key={item.id} data={item} />
))}
Without keys, React has to re-render the entire list when anything changes. With keys, React can efficiently update only the items that changed.
JSX // WITHOUT KEYS (BAD) — Inefficient
<ul>
{todos.map(todo => <li>{todo.text}</li>)}
</ul>
// Adding a todo at the beginning: React re-renders EVERYTHING
// WITH KEYS (GOOD) — Efficient
<ul>
{todos.map(todo => <li key={todo.id}>{todo.text}</li>)}
</ul>
// React sees the keys and only renders the new item
3.3 Where to Put Keys
JSX // CORRECT — key on mapped element
{items.map(item => (
<div key={item.id}><h3>{item.title}</h3></div>
))}
// CORRECT — key on Fragment
{items.map(item => (
<React.Fragment key={item.id}>
<h3>{item.title}</h3>
</React.Fragment>
))}
// WRONG — key on child, not mapped element
{items.map(item => (
<div><h3 key={item.id}>{item.title}</h3></div>
))}
3.4 What Makes a Good Key?
Good Keys Bad Keys
Database ID (user.id) Array index (dynamic lists)
UUID / unique SKU Math.random()
Unique field combo Timestamps that change
Guaranteed-unique email Values that can duplicate
3.5 What Makes a Bad Key?
Example 1: Using Index (Common Pitfall)
JSX function BadTodoList() {
const [todos, setTodos] = useState(["Task 1", "Task 2", "Task 3"]);
const addTodoAtBeginning = () => setTodos(["New Task", ...todos]);
return (
<ul>
{todos.map((todo, index) => (
<li key={index}>{todo}</li> // Problem: keys follow position, not data
))}
</ul>
);
}
Example 2: Using Random Values
Math.random() as key creates new keys every render — React destroys and recreates all DOM nodes (very slow).
JSX {items.map(item => (
<li key={Math.random()}>{item}</li> // New key every render!
))}
3.6 Key Behavior with map() Index (The Pitfall)
Using index as a key is safe only for static lists that never change order:
JSX // ACCEPTABLE — static list
const staticCountries = ["USA", "Canada", "Mexico"];
{staticCountries.map((country, index) => (
<option key={index} value={country}>{country}</option>
))}
// BUGGY — reorderable list with index as key
function BuggyTodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: "Learn React", completed: false },
{ id: 2, text: "Build project", completed: false }
]);
// When you reorder items, checkboxes get mixed up!
// Keys (indexes) stay with position, not with the data
}
4. Other Array Methods for Rendering
4.1 filter() and map() Combined
JSX const filteredProducts = category === "all"
? products
: products.filter(p => p.category === category);
{filteredProducts.map(product => (
<div key={product.id}>{product.name}</div>
))}
4.2 reduce() for Aggregated Rendering
JSX const grouped = products.reduce((groups, product) => {
if (!groups[product.category]) groups[product.category] = [];
groups[product.category].push(product);
return groups;
}, {});
{Object.entries(grouped).map(([category, items]) => (
<div key={category}>
<h2>{category}</h2>
{items.map(p => <div key={p.id}>{p.name}</div>)}
</div>
))}
4.3 sort() Before Rendering
JSX const sortedProducts = [...products].sort((a, b) =>
sortBy === "name" ? a.name.localeCompare(b.name) : a.price - b.price
);
JSX const startIndex = (currentPage - 1) * itemsPerPage;
const currentItems = items.slice(startIndex, startIndex + itemsPerPage);
{currentItems.map(item => <li key={item.id}>{item.name}</li>)}
5. Common List Rendering Patterns
5.1 Empty State Handling
JSX {cart.length === 0 ? (
<div className="empty-cart"><p>Your cart is empty</p></div>
) : (
<ul>{cart.map(item => <li key={item.id}>{item.name}</li>)}</ul>
)}
5.2 Loading Skeleton for Lists
JSX if (isLoading) {
return (
<div>
{[1, 2, 3, 4, 5].map(i => <div key={i} className="skeleton-item"></div>)}
</div>
);
}
5.3 Nested Lists
JSX {departments.map(dept => (
<div key={dept.id}>
<h3>{dept.name}</h3>
<ul>
{dept.employees.map(emp => (
<li key={emp.id}>{emp.name}</li>
))}
</ul>
</div>
))}
5.4 Search/Filter Lists
JSX const filteredProducts = products.filter(product => {
const matchesSearch = product.name.toLowerCase().includes(searchTerm.toLowerCase());
const matchesCategory = category === "all" || product.category === category;
return matchesSearch && matchesCategory;
});
{filteredProducts.length === 0 ? (
<p>No products match your search.</p>
) : (
filteredProducts.map(product => <li key={product.id}>{product.name}</li>)
)}
6. Complete Working Examples
6.1 Example 1: Product Catalog with Filtering
JSX function ProductCatalog() {
const [category, setCategory] = useState("all");
const [search, setSearch] = useState("");
const products = [/* id, name, category, price */];
const visible = products.filter(p =>
(category === "all" || p.category === category) &&
p.name.toLowerCase().includes(search.toLowerCase())
);
return (
<div>
<input value={search} onChange={e => setSearch(e.target.value)} />
<button onClick={() => setCategory("electronics")}>Electronics</button>
{visible.length === 0 ? <p>No products found</p> : (
visible.map(p => <ProductCard key={p.id} product={p} />)
)}
</div>
);
}
6.2 Example 2: Todo List with Add/Delete/Complete
JSX function TodoListApp() {
const [todos, setTodos] = useState([]);
const [text, setText] = useState("");
const addTodo = () => {
setTodos([...todos, { id: Date.now(), text, completed: false }]);
setText("");
};
const toggle = (id) => setTodos(todos.map(t =>
t.id === id ? { ...t, completed: !t.completed } : t
));
const remove = (id) => setTodos(todos.filter(t => t.id !== id));
return (
<div>
<input value={text} onChange={e => setText(e.target.value)} />
<button onClick={addTodo}>Add</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input type="checkbox" checked={todo.completed} onChange={() => toggle(todo.id)} />
{todo.text}
<button onClick={() => remove(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}
6.3 Example 3: User Directory with Search and Sorting
JSX function UserDirectory() {
const [search, setSearch] = useState("");
const [sortBy, setSortBy] = useState("name");
const users = [/* id, name, email, role */];
const displayed = [...users]
.filter(u => u.name.toLowerCase().includes(search.toLowerCase()))
.sort((a, b) => sortBy === "name" ? a.name.localeCompare(b.name) : a.role.localeCompare(b.role));
return (
<div>
<input value={search} onChange={e => setSearch(e.target.value)} />
<button onClick={() => setSortBy("name")}>Sort by Name</button>
<button onClick={() => setSortBy("role")}>Sort by Role</button>
{displayed.map(user => (
<UserRow key={user.id} user={user} />
))}
</div>
);
}
JSX function DynamicFormBuilder() {
const [fields, setFields] = useState([
{ id: 1, label: "Full Name", type: "text" }
]);
const addField = () => {
setFields([...fields, { id: Date.now(), label: "New Field", type: "text" }]);
};
const updateField = (id, key, value) => {
setFields(fields.map(f => f.id === id ? { ...f, [key]: value } : f));
};
const removeField = (id) => setFields(fields.filter(f => f.id !== id));
return (
<div>
<button onClick={addField}>Add Field</button>
{fields.map(field => (
<div key={field.id}>
<input value={field.label} onChange={e => updateField(field.id, "label", e.target.value)} />
<select value={field.type} onChange={e => updateField(field.id, "type", e.target.value)}>
<option value="text">Text</option>
<option value="email">Email</option>
<option value="number">Number</option>
</select>
<button onClick={() => removeField(field.id)}>Remove</button>
</div>
))}
</div>
);
}
7. Common Mistakes and Best Practices
Mistake Problem Solution
Index as key (dynamic lists) Reorder/filter bugs Use stable unique IDs
No key Warning, poor performance Always key mapped elements
Random keys Full re-renders Stable identifiers
Mutating before map State bugs Copy with [...array]
No empty state Broken UI Handle length === 0
Key on wrong element React can't track Key on direct mapped element
JSX // DO
{users.map(user => <UserCard key={user.id} user={user} />)}
const sorted = [...items].sort((a, b) => a.name.localeCompare(b.name));
{items.length === 0 && <EmptyState />}
// DON'T
{todos.map((todo, index) => <Todo key={index} todo={todo} />)}
{todos.map(todo => <Todo key={Math.random()} todo={todo} />)}
8. Summary Cheat Sheet
JSX // BASIC map()
{fruits.map(fruit => <li key={fruit}>{fruit}</li>)}
{users.map(user => <UserCard key={user.id} user={user} />)}
// GOOD vs BAD KEYS
key={user.id} // good
key={index} // bad for dynamic lists
key={Math.random()} // very bad
// FILTER THEN MAP
const active = users.filter(u => u.isActive);
{active.map(u => <User key={u.id} user={u} />)}
// SORT / PAGINATE
const sorted = [...products].sort((a, b) => a.price - b.price);
const page = items.slice(start, start + pageSize);
// EMPTY STATE
{items.length === 0 ? <EmptyState /> : items.map(i => <Item key={i.id} />)}
// NESTED LISTS
{depts.map(dept => (
<div key={dept.id}>
{dept.employees.map(emp => <li key={emp.id}>{emp.name}</li>)}
</div>
))}
Next Steps
useEffect Hook – Data fetching for dynamic lists
Forms and Controlled Components – Building forms with list data
React Router – List detail views
State Management – Context API, Redux for complex list state
Performance – useMemo, useCallback for large lists
React Lists & Keys MCQ Practice
10 Basic MCQs
10 Advanced MCQs
10 Basic React Lists & Keys MCQs
1
Rendering a list in React typically uses:
A array.map() to JSX elements
B for loop in JSX directly
C SQL SELECT
D CSS grid only
Explanation: {items.map(item => ...)}.
2
The key prop should be:
A Stable unique among siblings
B Math.random() every render
C Always index when list reorders
D Optional always
Explanation: Keys help reconciliation identify items.
3
Keys are passed to:
A Elements in the list returned from map
B Parent section only
C npm packages
D CSS files
Explanation: Put key on outermost element in map.
4
Using array index as key is risky when:
A Items can be reordered, inserted, or deleted
B List is static forever
C Items have no id ever
D Rendering text only once
Explanation: Index keys cause state bugs on reorder.
5
Fragment in map needs key when:
A Using <React.Fragment key={id}>
B Never needs key
C Only in CSS
D Using div always
Explanation: Short syntax <> cannot take key.
6
filter before map is used to:
A Show subset of items
B Replace keys
C Install React
D Disable Virtual DOM
Explanation: Chain filter then map for conditional lists.
7
Empty list UX often shows:
A Fallback message component
B Nothing required
C Error boundary only
D window.reload
Explanation: Handle items.length === 0.
8
key prop is:
A Not passed to child as prop
B Available as props.key always
C Same as id attribute always
D CSS class
Explanation: React consumes key—it is not visible in child props.
9
Nested lists need keys:
A On each list level's items
B Only on outer list
C Only on parent div
D Never
Explanation: Keys must be unique among immediate siblings.
10
map callback often returns:
A JSX element per item
B undefined only
C Promise
D CSS
Explanation: Return single root element per iteration.
10 Advanced React Lists & Keys MCQs
1
Best key for database records is usually:
A Primary id from database
B Array index always
C Random each render
D Item name duplicated
Explanation: Stable ids survive reorder.
2
Two lists same ids in different parents:
A Allowed—keys scoped to sibling list
B Global uniqueness required
C Causes crash always
D Forbidden by React
Explanation: Keys are local to each array parent.
3
Reconciliation with wrong keys can cause:
A Wrong component state attached to DOM node
B Faster renders always
C Automatic fix
D CSS loss only
Explanation: Mismatched keys reuse wrong internal state.
4
Virtualized long lists use:
A react-window or react-virtualized
B map without keys
C document.write
D tables only
Explanation: Render only visible rows for performance.
5
Sorting list before render should:
A Not mutate original state array in place
B Always mutate props
C Skip keys
D Use innerHTML
Explanation: Copy then sort: [...items].sort().
6
key on Component vs DOM element:
A Both valid on outer mapped element
B Only on strings
C Only in CSS
D Only in router
Explanation: Custom components receive key internally by React.
7
Grouping list items by category often uses:
A reduce or Map to sections then map
B Single key for all
C No keys
D global var
Explanation: Structure data then render nested lists.
8
Immutable update adding item:
A setItems([...items, newItem])
B items.push only without setState
C delete items
D key=index only
Explanation: New array reference triggers render.
9
Pagination changes keys when:
A Page changes item set—use stable ids not page index
B Never matters
C Always use Math.random
D Disable list
Explanation: Ids should identify entity not page offset.
10
React DevTools warning 'unique key' means:
A Missing or duplicate keys among siblings
B Invalid JSX
C Hook order wrong
D npm error
Explanation: Fix by adding stable unique keys.
Check All Answers
15 React Lists & Keys Interview Questions & Answers
Easy
Medium
Hard
1 Why do lists need keys?easy
Answer: So React can track item identity across renders for efficient updates.
2 Where to put the key prop?easy
Answer: On the outermost element returned inside map(), not on the map() call itself.
3 Can keys be duplicate among siblings?medium
Answer: No—duplicates confuse reconciliation and trigger warnings.
4 Why avoid index as key for dynamic lists?medium
Answer: Reordering changes index-to-item mapping, causing wrong state/DOM reuse.
5 Do keys need to be globally unique?medium
Answer: Only unique among siblings in that list, not entire app.
6 How to render filtered list?easy
Answer: items.filter(predicate).map(item => <Row key={item.id} />).
7 Fragment with key syntax?medium
Answer: <Fragment key={id}> or React.Fragment with key prop.
8 What happens without keys?medium
Answer: React warns and may reuse DOM nodes incorrectly for dynamic lists.
9 How to update one item in list state?medium
Answer: Map array replacing matching id with updated object.
10 Virtualization purpose?hard
Answer: Render subset of huge lists to keep DOM node count low.
11 Can key be passed to child as props.key?hard
Answer: No—key is reserved and not available in component props.
12 Nested map key strategy?hard
Answer: Use item.id at each level; avoid same index at multiple depths.
13 Empty array render best practice?easy
Answer: Show dedicated empty state UI.
14 Sort in render vs memoized sorted?hard
Answer: Memoize expensive sorts with useMemo when list is large.
15 Keys and animation libraries?hard
Answer: Stable keys help FLIP animations track elements correctly.