- Home
- /
- Tutorials
- /
- React Tutorial
- /
- Keys Explained
Lists and Keys
Keys Explained
A key is an identity, not a position. Using the array index says "the third item is whatever is currently third", which is exactly wrong the moment the list reorders.
What React uses them for
When a list re-renders, React matches old elements to new ones by key. A matched element is updated in place and keeps its state; an unmatched one is destroyed and rebuilt.
That matching is the entire purpose. Without keys React falls back to position, and warns you in the console because position is a poor guess.
Where index keys go wrong
This is easier to see than to describe. Both lists below hold the same data and differ only in their key. Type into the first input of each, then press Remove.
The classic index-key bug
jsx
function Row({ name }) {
const [note, setNote] = React.useState("")
return (
<li style={{ marginBottom: 4 }}>
{name}{" "}
<input
value={note}
placeholder="your note"
onChange={(e) => setNote(e.target.value)}
/>
</li>
)
}
function Demo() {
const [people, setPeople] = React.useState(["Ada", "Grace", "Alan"])
return (
<div>
<button onClick={() => setPeople((p) => p.slice(1))} disabled={people.length === 0}>
Remove the first person
</button>
<button onClick={() => setPeople(["Ada", "Grace", "Alan"])} style={{ marginLeft: 6 }}>
Reset
</button>
<div style={{ display: "flex", gap: 30, marginTop: 10 }}>
<div>
<b>key = index (wrong)</b>
<ul>{people.map((name, i) => <Row key={i} name={name} />)}</ul>
</div>
<div>
<b>key = name (right)</b>
<ul>{people.map((name) => <Row key={name} name={name} />)}</ul>
</div>
</div>
<p style={{ color: "#64748b" }}>
Type a note next to Ada in both lists, then remove the first person.
On the left the note stays behind with Grace. On the right it leaves with Ada.
</p>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(<Demo />)On the left, removing Ada does not remove her row - React sees key 0 still exists and simply changes its name to Grace, leaving the old input state attached. The note follows the position, not the person.
Choosing a key
- A database id, if the data has one. This is nearly always the answer.
- Any field that is unique and stable - a slug, an email, a filename.
- A generated id created when the item is created, not during render.
Never generate a key during render with Math.random() or Date.now(). It differs on every render, so React destroys and rebuilds every row every time - losing state and any focus the user had.
When index keys are fine
They are safe when all three hold: the list never reorders, items are never inserted or removed from the middle, and the items have no state or focus of their own.
A static list of labels rendered once qualifies. Anything a user can edit, sort or delete from does not - and since lists tend to gain those features later, a real id is the safer habit.
- Keys must be unique among siblings, not globally.
- A key belongs on the outermost element returned by the map.
- Keys are for React only - a component cannot read its own key as a prop.
- Changing a key deliberately resets a component, as in the previous chapter.
