Rendering Lists & Keys in React
Real applications rarely display just one item.
Think about:
A list of users
Products in an online store
Posts on social media
Comments
Messages
Notifications
Skills
Todo items
Search results
Usually, we have data like:
const users = [
{ id: 1, name: "Saurabh" },
{ id: 2, name: "Rahul" },
{ id: 3, name: "Aman" }
];
Instead of manually writing:
<h2>Saurabh</h2>
<h2>Rahul</h2>
<h2>Aman</h2>
React allows us to generate UI from the data.
The most common tool for this is JavaScript's:
.map()
And when rendering lists, React introduces another important concept:
key
Today we'll understand both.
๐ง What Does Rendering a List Mean?
Suppose we have:
const skills = [
"HTML",
"CSS",
"JavaScript",
"React"
];
We want:
HTML
CSS
JavaScript
React
Instead of writing each element manually, we can transform every item into JSX.
const skills = [
"HTML",
"CSS",
"JavaScript",
"React"
];
function App() {
return (
<ul>
{skills.map(skill => (
<li>{skill}</li>
))}
</ul>
);
}
The .map() method runs once for every item.
Conceptually:
skills
โ
.map()
โ
HTML โ <li>HTML</li>
CSS โ <li>CSS</li>
JavaScript โ <li>JavaScript</li>
React โ <li>React</li>
React then renders the resulting elements.
๐ Why .map() Is So Important
Remember:
.map()transforms an array into another array.
For example:
const numbers = [1, 2, 3];
const doubled = numbers.map(number => number * 2);
console.log(doubled);
Result:
[2, 4, 6]
In React, we're doing essentially the same thing.
Instead of:
number โ number * 2
we do:
item โ JSX
For example:
numbers.map(number => (
<li>{number}</li>
))
โ๏ธ Rendering an Array of Strings
Let's start with the simplest example.
function Skills() {
const skills = [
"HTML",
"CSS",
"JavaScript",
"React"
];
return (
<ul>
{skills.map(skill => (
<li key={skill}>
{skill}
</li>
))}
</ul>
);
}
Notice something important:
key={skill}
We'll understand why this is necessary shortly.
๐ What Is a key?
A key is a special React attribute that helps React identify individual items in a list.
For example:
<li key="html">HTML</li>
<li key="css">CSS</li>
<li key="javascript">JavaScript</li>
React can distinguish between them.
Think of keys like unique IDs.
HTML โ key: html
CSS โ key: css
JavaScript โ key: javascript
React uses these identities when determining how a list should be updated.
๐จ Why Does React Need Keys?
Imagine this list:
Apple
Banana
Mango
Then we insert:
Orange
at the beginning:
Orange
Apple
Banana
Mango
React needs a way to understand:
"These are the same existing items, but their positions changed."
Keys provide that identity.
Without stable keys, React has less information about which list item corresponds to which previous item.
โ Rendering Without a Key
You might write:
const users = ["Saurabh", "Rahul", "Aman"];
return (
<ul>
{users.map(user => (
<li>{user}</li>
))}
</ul>
);
React will warn that each child in a list should have a unique key.
The warning is telling you that the list items need stable identity.
โ Rendering With a Key
const users = ["Saurabh", "Rahul", "Aman"];
return (
<ul>
{users.map(user => (
<li key={user}>
{user}
</li>
))}
</ul>
);
Now each item has an identity.
However, this only works safely if the values themselves are unique and stable.
๐ The Best Key: A Unique ID
In real applications, data often looks like this:
const users = [
{
id: 101,
name: "Saurabh"
},
{
id: 102,
name: "Rahul"
},
{
id: 103,
name: "Aman"
}
];
Then:
function Users() {
return (
<ul>
{users.map(user => (
<li key={user.id}>
{user.name}
</li>
))}
</ul>
);
}
This is a much better pattern.
๐ฏ Why IDs Are Better
Suppose we have:
const users = [
{ id: 1, name: "Saurabh" },
{ id: 2, name: "Rahul" },
{ id: 3, name: "Saurabh" }
];
Names aren't unique.
So:
key={user.name}
would be problematic because two users have:
Saurabh
Instead:
key={user.id}
is unique.
โ ๏ธ Avoid Array Index as a Key
You may see:
users.map((user, index) => (
<li key={index}>
{user.name}
</li>
))
This works technically, but it can cause problems when list items can be:
Added
Removed
Reordered
Sorted
Filtered
For example:
Before:
0 โ Apple
1 โ Banana
2 โ Mango
After removing Apple:
0 โ Banana
1 โ Mango
The indexes changed.
The identity of the items didn't.
This is why a stable ID is generally preferred.
๐ง When Is Index Acceptable?
Using an index as a key isn't automatically forbidden.
It can be reasonable when:
The list is static
Items never reorder
Items are never inserted/removed
The list doesn't have its own stable IDs
Example:
const months = [
"January",
"February",
"March"
];
For a completely static list, an index may be acceptable.
But as a general rule:
If the data has a stable unique ID, use it.
๐ฆ Rendering Objects
Most real-world data consists of objects.
Example:
const products = [
{
id: 1,
name: "Keyboard",
price: 1500
},
{
id: 2,
name: "Mouse",
price: 800
},
{
id: 3,
name: "Monitor",
price: 12000
}
];
We can render them like this:
function Products() {
return (
<div>
{products.map(product => (
<div key={product.id}>
<h2>{product.name}</h2>
<p>โน{product.price}</p>
</div>
))}
</div>
);
}
This pattern is everywhere in React.
๐งฉ Lists + Components
Instead of putting everything inside .map(), we can create reusable components.
function ProductCard({ product }) {
return (
<div>
<h2>{product.name}</h2>
<p>โน{product.price}</p>
</div>
);
}
Then:
function ProductList({ products }) {
return (
<div>
{products.map(product => (
<ProductCard
key={product.id}
product={product}
/>
))}
</div>
);
}
This is a very important React pattern:
Array of Data
โ
.map()
โ
Reusable Component
โ
UI
โ ๏ธ Where Should key Go?
This is important.
Suppose:
products.map(product => (
<ProductCard product={product} />
))
The key should be placed on the outermost element/component created by the map.
Correct:
products.map(product => (
<ProductCard
key={product.id}
product={product}
/>
))
Not:
<ProductCard product={product}>
<div key={product.id}>
The key needs to identify the list item at the level where React is rendering the list.
๐ key Is Not a Normal Prop
A common beginner mistake is expecting:
function ProductCard({ key }) {
to receive the key.
It doesn't.
key is a special React attribute.
If your component needs the ID, pass it separately:
<ProductCard
key={product.id}
productId={product.id}
product={product}
/>
Then:
function ProductCard({ productId, product }) {
console.log(productId);
}
This distinction is important:
key
โ
React uses it internally
productId
โ
Your component receives it as a prop
๐ Rendering With .map() and Index
The callback receives:
array.map((item, index) => ...)
For example:
const skills = [
"HTML",
"CSS",
"JavaScript"
];
skills.map((skill, index) => (
<li key={skill}>
{index + 1}. {skill}
</li>
));
Output:
1. HTML
2. CSS
3. JavaScript
The index can still be useful for displaying the position.
Just don't automatically use it as the key when the list can change.
๐ Filtering Before Rendering
We can combine:
filter()
+
map()
Suppose:
const products = [
{ id: 1, name: "Keyboard", price: 1500 },
{ id: 2, name: "Mouse", price: 800 },
{ id: 3, name: "Monitor", price: 12000 }
];
We want products above โน1000:
const expensiveProducts = products.filter(
product => product.price > 1000
);
Then:
expensiveProducts.map(product => (
<div key={product.id}>
{product.name}
</div>
))
Or directly:
products
.filter(product => product.price > 1000)
.map(product => (
<div key={product.id}>
{product.name}
</div>
));
This is a powerful pattern.
๐ Searching Lists
Suppose:
const users = [
{ id: 1, name: "Saurabh" },
{ id: 2, name: "Rahul" },
{ id: 3, name: "Aman" }
];
We can filter based on search:
const filteredUsers = users.filter(user =>
user.name
.toLowerCase()
.includes(search.toLowerCase())
);
Then:
filteredUsers.map(user => (
<p key={user.id}>
{user.name}
</p>
))
This combination will become extremely useful when we start building real projects.
๐ญ Handling Empty Lists
What if:
const users = [];
We don't want to show an empty page.
We can use conditional rendering from Day 65:
{users.length > 0 ? (
users.map(user => (
<p key={user.id}>
{user.name}
</p>
))
) : (
<p>No users found.</p>
)}
Now our component handles both:
Users available
โ
Display list
No users
โ
Display empty state
This demonstrates how our React concepts build on each other.
๐ง Lists + Conditional Rendering
We can combine everything we've learned.
function ProductList({ products }) {
if (products.length === 0) {
return <p>No products available.</p>;
}
return (
<div>
{products.map(product => (
<div key={product.id}>
<h2>{product.name}</h2>
<p>โน{product.price}</p>
</div>
))}
</div>
);
}
Now we're using:
Components
Props
Conditional rendering
Arrays
.map()Keys
That's a lot of React fundamentals coming together.
๐งฑ Nested Lists
Sometimes data has nested arrays.
For example:
const users = [
{
id: 1,
name: "Saurabh",
skills: ["React", "Node.js"]
},
{
id: 2,
name: "Rahul",
skills: ["Python", "Django"]
}
];
We can render both levels:
function Users() {
return (
<div>
{users.map(user => (
<div key={user.id}>
<h2>{user.name}</h2>
<ul>
{user.skills.map(skill => (
<li key={skill}>
{skill}
</li>
))}
</ul>
</div>
))}
</div>
);
}
Each list needs appropriate keys.
๐ง Keys Are About Identity, Not Performance Alone
A common misconception is:
"Keys are just there to make React faster."
That's incomplete.
Keys primarily provide stable identity for list items so React can correctly determine which items correspond between renders.
They become especially important when list items:
Move
Change
Get inserted
Get deleted
So think:
Key = identity
rather than simply:
Key = performance optimization
๐ What Happens When a List Changes?
Suppose:
Before:
A
B
C
Then:
X
A
B
C
With stable keys:
X โ new
A โ existing
B โ existing
C โ existing
React can understand the identity of each item.
This helps it update the DOM appropriately.
๐จ Avoid Random Keys
Don't do this:
key={Math.random()}
Every render generates a new key.
That means React sees different identities every time.
This can cause unnecessary recreation of list items and can lead to problems with component state.
Use stable keys instead.
๐ Best Practices
1. Use stable unique IDs
Best:
key={user.id}
2. Avoid indexes for dynamic lists
Instead of:
key={index}
prefer:
key={item.id}
when an ID exists.
3. Never use random keys
Avoid:
key={Math.random()}
4. Put the key on the mapped element
items.map(item => (
<Card key={item.id} />
))
5. Don't expect key as a prop
If the component needs an ID:
<Card
key={item.id}
id={item.id}
/>
6. Keep list rendering readable
If the JSX inside .map() becomes too large, extract a component.
๐ Complete Example: Todo List
Let's combine today's concepts into a realistic example.
import { useState } from "react";
function App() {
const [tasks] = useState([
{
id: 1,
title: "Learn React"
},
{
id: 2,
title: "Practice JavaScript"
},
{
id: 3,
title: "Build a project"
}
]);
return (
<div>
<h1>My Tasks</h1>
{tasks.length > 0 ? (
<ul>
{tasks.map(task => (
<li key={task.id}>
{task.title}
</li>
))}
</ul>
) : (
<p>No tasks available.</p>
)}
</div>
);
}
export default App;
This tiny application contains:
State
โ
Array
โ
Conditional Rendering
โ
.map()
โ
Keys
โ
UI
๐ก My Biggest Takeaway
Today's biggest takeaway is:
React allows us to describe UI based on data rather than manually creating every element.
Instead of:
<Card />
<Card />
<Card />
<Card />
we can have:
products.map(product => (
<Card
key={product.id}
product={product}
/>
))
Now the UI automatically reflects the data.
And the key concept is:
Array of Data
โ
map()
โ
React Elements
โ
key
โ
Correct List Identity
This is one of the patterns I'll use constantly while building React applications.
