
ReactJS
// β WRONG - Using index in a list that can reorder
function TodoList() {
const [todos, setTodos] = React.useState([
{ id: 1, text: "Buy milk" },
{ id: 2, text: "Walk dog" }
]);
const addTodo = (text: string) => {
// Adding to the beginning!
setTodos([{ id: Math.random(), text }, ...todos]);
};
return (
<ul>
{todos.map((todo, index) => (
<li key={index}> // β Index changes when items are added
<input type="checkbox" />
{todo.text}
</li>
))}
</ul>
);
}1. First render: `key=0` points to "Buy milk", `key=1` points to "Walk dog"
2. You add a new item at the beginning
3. Now `key=0` points to the new item, but React thinks it's the same as before
4. Component state (like that checkbox) gets attached to the wrong item
// β
CORRECT - Use stable ID
function TodoList() {
const [todos, setTodos] = React.useState([
{ id: 1, text: "Buy milk" },
{ id: 2, text: "Walk dog" }
]);
const addTodo = (text: string) => {
setTodos([{ id: Date.now(), text }, ...todos]); // Each ID is unique
};
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}> // β
Stable ID that won't change
<input type="checkbox" />
{todo.text}
</li>
))}
</ul>
);
}// β WRONG - Generating key with Math.random()
function MessageList() {
const messages = ["Hello", "World", "React"];
return (
<ul>
{messages.map((message) => (
<li key={Math.random()}> // β New key every render!
{message}
</li>
))}
</ul>
);
}Every time the component re-renders:
// β
CORRECT - Generate ID once, not per render
function MessageList() {
const [messages] = React.useState(() =>
["Hello", "World", "React"].map((msg, idx) => ({
id: idx, // Or use uuidv4() in real apps
text: msg
}))
);
return (
<ul>
{messages.map((message) => (
<li key={message.id}> // β
Same key every render
{message.text}
</li>
))}
</ul>
);
}// β WRONG - Creating key from changing values
function UserItems() {
const [order, setOrder] = React.useState("asc");
const items = [
{ id: 1, name: "Alice", score: 100 },
{ id: 2, name: "Bob", score: 85 },
{ id: 3, name: "Carol", score: 92 }
];
const sorted = order === "asc"
? [...items].sort((a, b) => a.score - b.score)
: [...items].sort((a, b) => b.score - a.score);
return (
<div>
<button onClick={() => setOrder(order === "asc" ? "desc" : "asc")}>
Sort {order}
</button>
<ul>
{sorted.map((item, index) => (
<li key={`${item.name}-${index}`}> // β Index changes when sorting!
{item.name}: {item.score}
</li>
))}
</ul>
</div>
);
}// β
CORRECT - Use stable ID regardless of sort order
function UserItems() {
const [order, setOrder] = React.useState("asc");
const items = [
{ id: 1, name: "Alice", score: 100 },
{ id: 2, name: "Bob", score: 85 },
{ id: 3, name: "Carol", score: 92 }
];
const sorted = order === "asc"
? [...items].sort((a, b) => a.score - b.score)
: [...items].sort((a, b) => b.score - a.score);
return (
<div>
<button onClick={() => setOrder(order === "asc" ? "desc" : "asc")}>
Sort {order}
</button>
<ul>
{sorted.map((item) => (
<li key={item.id}> // β
ID stays the same regardless of order
{item.name}: {item.score}
</li>
))}
</ul>
</div>
);
}// β WRONG - No key at all
function ItemList() {
const items = ["React", "Vue", "Angular"];
return (
<ul>
{items.map((item) => (
<li>{item}</li> // β React uses index as fallback key
))}
</ul>
);
}Without a key, React falls back to using the index. This causes all the problems we discussed above.
// β
CORRECT - Always provide a key
function ItemList() {
const items = [
{ id: 1, name: "React" },
{ id: 2, name: "Vue" },
{ id: 3, name: "Angular" }
];
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li> // β
Explicit key
))}
</ul>
);
}<details><summary>π More Examples</summary>
// β WRONG - Key depends on parent prop
interface ListProps {
baseId: string;
items: string[];
}
function BadKeyList({ baseId, items }: ListProps) {
return (
<ul>
{items.map((item, index) => (
<li key={`${baseId}-${index}`}> // β baseId changes = key changes
{item}
</li>
))}
</ul>
);
}
// If baseId changes, all keys change even though items are the same!
// β
CORRECT
function GoodKeyList({ baseId, items }: ListProps) {
const itemList = items.map((item, idx) => ({
id: idx, // Or get real IDs from data
value: item
}));
return (
<ul>
{itemList.map((item) => (
<li key={item.id}> // β
Independent of props
{item.value}
</li>
))}
</ul>
);
}// β WRONG - Index changes when filtering
function ContactsList() {
const [filter, setFilter] = React.useState("");
const [contacts, setContacts] = React.useState([
{ id: 1, name: "Alice", email: "alice@example.com" },
{ id: 2, name: "Bob", email: "bob@example.com" },
{ id: 3, name: "Carol", email: "carol@example.com" }
]);
const filtered = contacts.filter(c =>
c.name.toLowerCase().includes(filter.toLowerCase())
);
return (
<div>
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="Filter..."
/>
<ul>
{filtered.map((contact, index) => (
<li key={index}> // β When filtered, indices change
<EditableContact contact={contact} />
</li>
))}
</ul>
</div>
);
}
// β
CORRECT
function CorrectContactsList() {
const [filter, setFilter] = React.useState("");
const [contacts, setContacts] = React.useState([
{ id: 1, name: "Alice", email: "alice@example.com" },
{ id: 2, name: "Bob", email: "bob@example.com" },
{ id: 3, name: "Carol", email: "carol@example.com" }
]);
const filtered = contacts.filter(c =>
c.name.toLowerCase().includes(filter.toLowerCase())
);
return (
<div>
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="Filter..."
/>
<ul>
{filtered.map((contact) => (
<li key={contact.id}> // β
ID stays stable when filtering
<EditableContact contact={contact} />
</li>
))}
</ul>
</div>
);
}</details>
| Mistake | Example | Problem | Fix |
|---|---|---|---|
| Index in dynamic list | `key={index}` | State gets shuffled | Use unique ID |
| Random key | `key={Math.random()}` | Key changes every render | Store ID in state |
| Unstable composite | `key={name + index}` | Changes when sorted | Use stable ID only |
| No key | (no key prop) | Falls back to index | Always add key |
| Key from prop | `key={parentId + index}` | Changes with parent | Use item's own ID |
1. β Never use index as key for lists that can reorder, filter, or change
2. β Never generate keys with Math.random() or similar functions
3. β Keys must be the same across all renders for the same item
4. β Always include a key prop, especially with dynamic lists
5. β Use unique IDs from your dataβdatabase IDs are best
6. β Composite keys should combine stable values only
7. β Don't base keys on props that change
8. β The key must identify the item, not its position
9. β When in doubt, use the item's ID from your data source
10. β Bad keys don't cause errors but cause subtle bugs in component state
Now let's look at how to filter lists correctly without breaking your keys.
β Learn about Filtering Lists
Challenge: Fix broken keys in a list
Resources
Ojasa Mirai
Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.
Learn Deep β’ Build Real β’ Verify Skills β’ Launch Forward