React Lists & Keys

Render dynamic lists with map(), keys, filter, sort, and pagination

map() Keys filter() Pagination

Table of Contents

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

JavaScriptconst 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

JSXfunction 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

JSXfunction 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.

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

3.2 Why Keys are Important (Performance)

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 KeysBad Keys
Database ID (user.id)Array index (dynamic lists)
UUID / unique SKUMath.random()
Unique field comboTimestamps that change
Guaranteed-unique emailValues that can duplicate

3.5 What Makes a Bad Key?

Example 1: Using Index (Common Pitfall)

JSXfunction 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

JSXconst 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

JSXconst 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

JSXconst sortedProducts = [...products].sort((a, b) => sortBy === "name" ? a.name.localeCompare(b.name) : a.price - b.price );

4.4 slice() for Pagination

JSXconst 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

JSXif (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

JSXconst 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

JSXfunction 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

JSXfunction 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

JSXfunction 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> ); }

6.4 Example 4: Dynamic Form Builder

JSXfunction 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

MistakeProblemSolution
Index as key (dynamic lists)Reorder/filter bugsUse stable unique IDs
No keyWarning, poor performanceAlways key mapped elements
Random keysFull re-rendersStable identifiers
Mutating before mapState bugsCopy with [...array]
No empty stateBroken UIHandle length === 0
Key on wrong elementReact can't trackKey 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:

Aarray.map() to JSX elements
Bfor loop in JSX directly
CSQL SELECT
DCSS grid only
Explanation: {items.map(item => ...)}.
2

The key prop should be:

AStable unique among siblings
BMath.random() every render
CAlways index when list reorders
DOptional always
Explanation: Keys help reconciliation identify items.
3

Keys are passed to:

AElements in the list returned from map
BParent section only
Cnpm packages
DCSS files
Explanation: Put key on outermost element in map.
4

Using array index as key is risky when:

AItems can be reordered, inserted, or deleted
BList is static forever
CItems have no id ever
DRendering text only once
Explanation: Index keys cause state bugs on reorder.
5

Fragment in map needs key when:

AUsing <React.Fragment key={id}>
BNever needs key
COnly in CSS
DUsing div always
Explanation: Short syntax <> cannot take key.
6

filter before map is used to:

AShow subset of items
BReplace keys
CInstall React
DDisable Virtual DOM
Explanation: Chain filter then map for conditional lists.
7

Empty list UX often shows:

AFallback message component
BNothing required
CError boundary only
Dwindow.reload
Explanation: Handle items.length === 0.
8

key prop is:

ANot passed to child as prop
BAvailable as props.key always
CSame as id attribute always
DCSS class
Explanation: React consumes key—it is not visible in child props.
9

Nested lists need keys:

AOn each list level's items
BOnly on outer list
COnly on parent div
DNever
Explanation: Keys must be unique among immediate siblings.
10

map callback often returns:

AJSX element per item
Bundefined only
CPromise
DCSS
Explanation: Return single root element per iteration.

10 Advanced React Lists & Keys MCQs

1

Best key for database records is usually:

APrimary id from database
BArray index always
CRandom each render
DItem name duplicated
Explanation: Stable ids survive reorder.
2

Two lists same ids in different parents:

AAllowed—keys scoped to sibling list
BGlobal uniqueness required
CCauses crash always
DForbidden by React
Explanation: Keys are local to each array parent.
3

Reconciliation with wrong keys can cause:

AWrong component state attached to DOM node
BFaster renders always
CAutomatic fix
DCSS loss only
Explanation: Mismatched keys reuse wrong internal state.
4

Virtualized long lists use:

Areact-window or react-virtualized
Bmap without keys
Cdocument.write
Dtables only
Explanation: Render only visible rows for performance.
5

Sorting list before render should:

ANot mutate original state array in place
BAlways mutate props
CSkip keys
DUse innerHTML
Explanation: Copy then sort: [...items].sort().
6

key on Component vs DOM element:

ABoth valid on outer mapped element
BOnly on strings
COnly in CSS
DOnly in router
Explanation: Custom components receive key internally by React.
7

Grouping list items by category often uses:

Areduce or Map to sections then map
BSingle key for all
CNo keys
Dglobal var
Explanation: Structure data then render nested lists.
8

Immutable update adding item:

AsetItems([...items, newItem])
Bitems.push only without setState
Cdelete items
Dkey=index only
Explanation: New array reference triggers render.
9

Pagination changes keys when:

APage changes item set—use stable ids not page index
BNever matters
CAlways use Math.random
DDisable list
Explanation: Ids should identify entity not page offset.
10

React DevTools warning 'unique key' means:

AMissing or duplicate keys among siblings
BInvalid JSX
CHook order wrong
Dnpm error
Explanation: Fix by adding stable unique keys.

15 React Lists & Keys Interview Questions & Answers

Easy Medium Hard
1Why do lists need keys?easy
Answer: So React can track item identity across renders for efficient updates.
2Where to put the key prop?easy
Answer: On the outermost element returned inside map(), not on the map() call itself.
3Can keys be duplicate among siblings?medium
Answer: No—duplicates confuse reconciliation and trigger warnings.
4Why avoid index as key for dynamic lists?medium
Answer: Reordering changes index-to-item mapping, causing wrong state/DOM reuse.
5Do keys need to be globally unique?medium
Answer: Only unique among siblings in that list, not entire app.
6How to render filtered list?easy
Answer: items.filter(predicate).map(item => <Row key={item.id} />).
7Fragment with key syntax?medium
Answer: <Fragment key={id}> or React.Fragment with key prop.
8What happens without keys?medium
Answer: React warns and may reuse DOM nodes incorrectly for dynamic lists.
9How to update one item in list state?medium
Answer: Map array replacing matching id with updated object.
10Virtualization purpose?hard
Answer: Render subset of huge lists to keep DOM node count low.
11Can key be passed to child as props.key?hard
Answer: No—key is reserved and not available in component props.
12Nested map key strategy?hard
Answer: Use item.id at each level; avoid same index at multiple depths.
13Empty array render best practice?easy
Answer: Show dedicated empty state UI.
14Sort in render vs memoized sorted?hard
Answer: Memoize expensive sorts with useMemo when list is large.
15Keys and animation libraries?hard
Answer: Stable keys help FLIP animations track elements correctly.