In React, every item rendered in a list should have a unique key. This helps React efficiently update, add, or remove list items in the DOM without breaking UI or state.
A common shortcut is to use the array index as the key (for example, key={index}). While this might seem to “work”, it can create subtle bugs when items are added, removed, or reordered.
Understanding when index keys are safe—and when they are dangerous—is important for building predictable, stable React components.
? Core idea: Prefer stable, unique IDs over array indexes.
When rendering a list in React, you typically write something like:
{items.map(item => (<li key={item.id}>{item.label}</li>))}
key must be unique among siblings.Here’s a simple list where the array index is used as the key:
// Using index as key in a simple list (safe only when list is static)
function NameList() {
const names = ["Alice", "Bob", "Charlie"];
return (
<ul>
{names.map((name, index) => (
<li key={index}>{name}</li>
))}
</ul>
);
}
This component works correctly as long as the list is static and never changes. But as soon as you add, remove, or reorder items, using index as the key can introduce mismatches between items and their internal state.
Consider a list of editable inputs where each item uses its index as the key:
// Using index as key in a dynamic list of inputs (dangerous)
function EditableList() {
const [items, setItems] = React.useState(["Apple", "Banana", "Cherry"]);
const removeItem = (indexToRemove) => {
setItems(items.filter((_, index) => index !== indexToRemove));
};
return (
<ul>
{items.map((item, index) => (
<li key={index}>
<input defaultValue={item} />
<button onClick={() => removeItem(index)}>❌</button>
</li>
))}
</ul>
);
}
This happens because React assumes items with the same key are the “same” item between renders. When keys shift due to index-based keys, that assumption breaks.
For a list ["A", "B", "C"] with index keys [0, 1, 2]:
[0, 1] and reuses the underlying DOM nodes.The UI still renders, but the component state no longer matches the logical data, causing confusing bugs.
The recommended approach is to give each item a stable, unique identifier (such as an id field) and use that as the key:
// Using a stable id for each item keeps inputs and state aligned
function SafeList() {
const [fruits, setFruits] = React.useState([
{ id: 1, name: "Apple" },
{ id: 2, name: "Banana" },
{ id: 3, name: "Cherry" },
]);
return (
<ul>
{fruits.map((fruit) => (
<li key={fruit.id}>
<input defaultValue={fruit.name} />
</li>
))}
</ul>
);
}
Now, even if you insert, remove, or reorder items, each fruit keeps the same id and therefore the same key, so React preserves the correct state for each list item.
Using the array index as a key is acceptable in a few specific cases:
// Safe: static, read-only list using index as key
const colors = ["Red", "Green", "Blue"];
<ul>
{colors.map((color, index) => (
<li key={index}>{color}</li>
))}
</ul>
For dynamic lists like todos or form rows, generate a unique ID when the item is created (for example, using Date.now() or a UUID library) and keep using that ID as the key:
// Dynamic todo list using Date.now() to generate stable ids
function TodoList() {
const [todos, setTodos] = React.useState([
{ id: Date.now(), task: "Learn React" },
]);
const addTodo = () => {
const newTodo = { id: Date.now(), task: "New Task" };
setTodos([...todos, newTodo]);
};
return (
<div>
<ul>
{todos.map((t) => (
<li key={t.id}>{t.task}</li>
))}
</ul>
<button className="btn btn-primary" onClick={addTodo}>Add</button>
</div>
);
}
id at the moment of creation.t.id as the key, so each list item’s DOM and state are stable.| Scenario | Index as Key | Unique ID as Key |
|---|---|---|
| Static list (no changes) | ✅ Safe | ✅ Safe |
| Dynamic list (add/remove) | ❌ Can cause UI/state issues | ✅ Preserves stability |
| Reordering elements | ❌ Risk of mismatched items | ✅ React reuses components correctly |
| Performance & state consistency | ⚠️ Unstable, can cause subtle bugs | ✅ Reliable, predictable behavior |
key={index} and another using key={item.id}. Reorder items and compare how state behaves.Date.now() or UUIDs as keys and confirm that state stays attached to the correct items.Goal: Develop an intuition for when index keys are safe and how stable, unique keys improve React’s rendering accuracy and user experience.