Common Key Mistakes
Keys are one of the most misunderstood parts of React. Getting them wrong doesn't always cause an immediate error — sometimes the bugs are subtle and only appear under specific conditions like reordering or deleting items. This page covers the most common key mistakes, shows the exact bug each one causes, and explains the correct fix.
Mistake 1: Using Array Index as Key
The most common mistake. Index keys work fine for static lists, but break when items are inserted, deleted, or reordered — because the index changes but the DOM nodes don't move.
// ✗ Bug: index keys cause stale state in inputs during reorder
function ContactList() {
const [contacts, setContacts] = useState([
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Carol' },
])
function removeFirst() {
setContacts(prev => prev.slice(1))
}
return (
<>
<button onClick={removeFirst}>Remove first</button>
<ul>
{contacts.map((c, index) => (
<li key={index}>
{c.name} <input placeholder="Note" />
</li>
))}
</ul>
</>
)
}
// Bug scenario:
// 1. Type "note about Alice" in the first input
// 2. Click "Remove first"
// 3. Alice is removed — but the text "note about Alice" is now in Bob's input
// because React matched key=0 (Bob) to the old key=0 (Alice) DOM node
// ✓ Fix: use the stable ID from your data
{contacts.map(c => (
<li key={c.id}>
{c.name} <input placeholder="Note" />
</li>
))}When Index Keys Are Actually Fine
Index keys are safe when all three conditions are met:
The list is static — items are never added, removed, or reordered
The items have no internal state (no inputs, no controlled components inside them)
The list is never re-rendered with different items at the same positions
// ✓ Fine: static list of display-only items that never change order
const STEPS = ['Install', 'Configure', 'Run', 'Deploy']
function InstallGuide() {
return (
<ol>
{STEPS.map((step, index) => (
<li key={index}>{step}</li>
))}
</ol>
)
}
// ✓ Also fine: display-only list, no inputs inside items
function TagList({ tags }) {
return (
<div>
{tags.map((tag, index) => (
<span key={index} className="tag">{tag}</span>
))}
</div>
)
}Mistake 2: Generating Random Keys on Each Render
Some developers try to fix missing-key warnings by generating random keys. This is worse than using index keys — it causes every item to remount on every render.
// ✗ Very bad: new random key on every render = full remount every time
function BadList({ items }) {
return (
<ul>
{items.map(item => (
<li key={Math.random()}>{item.name}</li>
))}
</ul>
)
}
// What happens on every render:
// - Every item gets a new key
// - React sees all keys as "new" (never seen before)
// - React unmounts all old items, mounts all new items
// - Input values, focus state, animations — all reset
// - Performance is terrible for large lists
// ✗ Also bad: Date.now() has the same problem
<li key={Date.now() + index}>{item.name}</li>
// ✓ Fix: assign IDs when creating the data, use them as keys
function createItem(name) {
return { id: crypto.randomUUID(), name }
}
// Then: items.map(item => <li key={item.id}>{item.name}</li>)Mistake 3: Putting Key on the Wrong Element
The key prop must be on the outermost element returned from the map() callback — not on an inner element. If you're using a Fragment as the wrapper, the key goes on the Fragment.
// ✗ Bug: key is on the inner element, not the outer one
function List({ items }) {
return (
<ul>
{items.map(item => (
<li>
<span key={item.id}>{item.name}</span>
</li>
))}
</ul>
)
}
// ✓ Fix: key goes on the outermost element of the map callback
function List({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>
<span>{item.name}</span>
</li>
))}
</ul>
)
}
// ✓ Key on Fragment (use the long form, not <> </>, which can't take props)
function List({ items }) {
return (
<dl>
{items.map(item => (
<Fragment key={item.id}>
<dt>{item.label}</dt>
<dd>{item.value}</dd>
</Fragment>
))}
</dl>
)
}Mistake 4: Non-Unique Keys in the Same List
Keys must be unique among siblings. If two items in the same list share a key, React will only render one of them and you'll get a console warning — plus unpredictable behaviour.
// ✗ Bug: duplicate keys — React warns and may skip items
const items = [
{ id: 1, name: 'Alice' },
{ id: 1, name: 'Bob' }, // same ID!
{ id: 2, name: 'Carol' },
]
// ✗ Also: constructing non-unique keys incorrectly
{items.map(item => (
<li key="item">{item.name}</li> // all keys are literally "item"
))}
// ✓ Fix: ensure IDs are truly unique in your data
// If combining multiple data sources that might have overlapping IDs:
{[...users, ...admins].map(person => (
<li key={`${person.type}-${person.id}`}>{person.name}</li>
))}Mistake 5: Expecting to Read the Key Prop
key is consumed by React and is not accessible as props.key inside your component. This surprises many developers who try to use it to identify which item is being rendered.
// ✗ Bug: props.key is always undefined
function Item({ key, name }) {
console.log(key) // undefined — React does not pass key as a prop
return <li>{name}</li>
}
// ✓ Fix: pass the ID explicitly as a separate prop
function Item({ id, name }) {
console.log(id) // works correctly
return <li>{name}</li>
}
function List({ items }) {
return (
<ul>
{items.map(item => (
<Item
key={item.id} // consumed by React for reconciliation
id={item.id} // passed to the component
name={item.name}
/>
))}
</ul>
)
}Mistake 6: Using Key to Force a Reset (Misuse)
One legitimate use of keys is to force a component to reset by changing its key — this causes React to unmount and remount it. However, misusing this pattern is a common mistake:
// ✓ Intentional key trick: reset a form when selectedUser changes
// Changing the key forces the child to fully remount and reset its state
function EditForm({ selectedUser }) {
return <UserForm key={selectedUser.id} user={selectedUser} />
}
// ✗ Misuse: changing key unnecessarily causes performance problems
function Counter({ label }) {
// Don't do this — you're remounting on every label change
return <CounterInner key={label} label={label} />
}
// The key trick is appropriate when you genuinely want to destroy
// and recreate a component's state. For most updates, just let
// React update the component normally.Quick Reference: Key Dos and Don'ts
Do use stable IDs from your data (database IDs, UUIDs, slugs)
Do put the key on the outermost element of the
map()callbackDo use index keys for static, display-only lists that never change order
Don't use
Math.random()orDate.now()as keysDon't use array index as key for lists that can be reordered, filtered, or that have inputs
Don't use duplicate keys within the same list
Don't try to read
props.key— pass the ID explicitly if you need itDon't put the key on an inner element — it belongs on the outermost element returned from
map()