This warning occurs when rendering an array of elements without assigning a unique "key" prop to each element. Keys help React identify which items have changed, been added, or removed, enabling efficient updates and preventing state mismatches.
This warning is triggered when React detects that you're rendering an array of elements (typically using .map()) without providing a unique "key" prop to each element. React's reconciliation algorithm relies on keys to track element identity across renders, which allows it to determine what has changed and update the DOM efficiently. Without keys, React falls back to using element position in the array to match elements between renders. This can cause serious issues when the array order changes, items are added or removed, or the list is filtered or sorted. Components may lose their state, animations may break, or the wrong elements may be updated. The warning appears in the browser console during development and indicates a potential performance and correctness issue that should be addressed before deploying to production.
When using .map() to render an array of elements, add a unique key prop to the outermost element returned from the map function:
// ❌ Missing key
const listItems = users.map(user =>
<li>{user.name}</li>
);
// ✅ Correct - key added
const listItems = users.map(user =>
<li key={user.id}>{user.name}</li>
);The key should be a stable, unique identifier from your data (like a database ID), not the array index.
If you're wrapping elements in a Fragment or custom component, the key must go on the outermost element:
// ❌ Key on wrong element
const items = data.map(item => (
<div>
<h3 key={item.id}>{item.title}</h3>
<p>{item.description}</p>
</div>
));
// ✅ Key on wrapper div
const items = data.map(item => (
<div key={item.id}>
<h3>{item.title}</h3>
<p>{item.description}</p>
</div>
));
// ✅ Key on Fragment (using explicit Fragment syntax)
import { Fragment } from 'react';
const items = data.map(item => (
<Fragment key={item.id}>
<h3>{item.title}</h3>
<p>{item.description}</p>
</Fragment>
));Note: You cannot use the shorthand Fragment syntax <>...</> with keys; use <Fragment key={...}> instead.
Choose keys that uniquely identify each item and remain stable across renders:
// ✅ Best - database ID or unique identifier
const items = products.map(product => (
<ProductCard key={product.id} product={product} />
));
// ✅ Good - composite key if no single unique field
const items = comments.map(comment => (
<Comment key={`${comment.userId}-${comment.timestamp}`} comment={comment} />
));
// ⚠️ Acceptable only for static lists that never reorder
const items = staticItems.map((item, index) => (
<div key={index}>{item}</div>
));
// ❌ Bad - array index for dynamic lists
const items = todos.map((todo, index) => (
<Todo key={index} todo={todo} /> // State issues when reordering!
));Avoid using array indices as keys unless the list is truly static (never filtered, reordered, or modified).
If your data doesn't have unique IDs, you have several options:
Option 1: Generate stable IDs when data is loaded
import { nanoid } from 'nanoid';
// Add IDs when fetching data
const itemsWithIds = rawItems.map(item => ({
...item,
id: nanoid() // Generate once and store
}));
// Later when rendering
const list = itemsWithIds.map(item => (
<Item key={item.id} item={item} />
));Option 2: Use a hash of the item's content
import { hash } from 'some-hash-library';
const items = data.map(item => (
<Item
key={hash(JSON.stringify(item))}
item={item}
/>
));Option 3: Index is acceptable for truly static lists
// OK if list never changes
const FIXED_MENU_ITEMS = ['Home', 'About', 'Contact'];
const menuItems = FIXED_MENU_ITEMS.map((label, index) => (
<MenuItem key={index} label={label} />
));Choose the approach that best fits your data characteristics and list behavior.
If you have ESLint configured with eslint-plugin-react, enable the react/jsx-key rule to catch missing keys:
// .eslintrc.json
{
"extends": ["plugin:react/recommended"],
"rules": {
"react/jsx-key": ["error", {
"checkFragmentShorthand": true,
"checkKeyMustBeforeSpread": true,
"warnOnDuplicates": true
}]
}
}This will show errors in your editor before runtime, helping you catch missing keys during development.
Run ESLint to check for violations:
npx eslint src/**/*.{js,jsx,tsx}Why React Needs Keys: Understanding Reconciliation
React uses a "reconciliation" algorithm to efficiently update the DOM. When state changes, React creates a new virtual DOM tree and compares it to the previous one. Keys serve as hints to the reconciliation algorithm, telling React which elements correspond to which items in your data.
Without keys, React uses element position in the array. If you insert an item at the beginning of a list, React sees that every position now contains a different element and re-renders the entire list unnecessarily. With proper keys, React recognizes that the existing elements just shifted position and only creates the new DOM node for the inserted item.
Why Array Index Keys Are Problematic
Using array indices as keys works fine for static lists, but causes issues for dynamic lists:
// Initial render with index keys
todos = [{text: "Learn React"}, {text: "Build app"}]
// React sees: <Todo key={0} />, <Todo key={1} />
// After adding item at the beginning
todos = [{text: "Read docs"}, {text: "Learn React"}, {text: "Build app"}]
// React sees: <Todo key={0} />, <Todo key={1} />, <Todo key={2} />
// React thinks key={0} just changed its content (wrong!)
// State from old key={0} now applies to key={0} (which is the new item)This breaks component state, causes wrong inputs to receive focus, and degrades performance.
Keys Must Be Unique Among Siblings Only
Keys only need to be unique among their sibling elements, not globally:
function App() {
return (
<div>
<UserList users={users} /> {/* IDs like "user-1", "user-2" */}
<PostList posts={posts} /> {/* IDs like "post-1", "post-2" */}
</div>
);
}The UserList and PostList can both have items with key="1" without conflict because they're in different arrays.
Keys Are Not Accessible as Props
The key prop is special and not passed to components:
function Item({ key, name }) { // ❌ key is undefined
return <div>{key}: {name}</div>;
}
// ✅ Pass ID separately if you need it
function Item({ id, name }) {
return <div>{id}: {name}</div>;
}
<Item key={item.id} id={item.id} name={item.name} />If you need to access the identifier inside your component, pass it as a separate prop with a different name (like id).
Performance Impact
Proper keys improve performance by:
- Reducing unnecessary DOM operations (React updates only what changed)
- Preserving component state across renders
- Enabling better animation and transition behavior
- Allowing React to reuse DOM nodes instead of destroying and recreating them
In large lists (100+ items), the performance difference between index keys and proper unique keys can be significant, especially when frequently reordering or filtering.
Keys in Component Composition
When extracting list rendering into a separate component, the key goes where the component is used, not in its definition:
// ❌ Key inside component definition (wrong place)
function TodoItem({ todo }) {
return <li key={todo.id}>{todo.text}</li>;
}
// In parent
<ul>
{todos.map(todo => <TodoItem todo={todo} />)} {/* Missing key! */}
</ul>
// ✅ Key where component is used
function TodoItem({ todo }) {
return <li>{todo.text}</li>;
}
// In parent
<ul>
{todos.map(todo => (
<TodoItem key={todo.id} todo={todo} /> {/* Correct */}
))}
</ul>This is because the parent needs to track the array of TodoItem components, not the <li> elements inside each TodoItem.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite