This React warning occurs when rendering an array of elements without providing a unique key prop for each item. Keys help React efficiently identify which items have changed, been added, or removed during re-renders.
This warning appears when you're rendering a list of React components or elements without providing the required key prop. React displays this warning in development mode whenever you use Array.map(), Array.filter(), or similar methods to render multiple elements without assigning a unique key to each one. The key prop is a special attribute that React uses internally to track each element's identity across renders. When React re-renders a list, it uses these keys to determine which items have changed, which are new, and which have been removed. Without keys, React can't efficiently update the DOM and may end up unnecessarily re-rendering all items, even if only one changed. This affects both performance and can cause subtle bugs like incorrect UI updates, lost component state, or form inputs losing focus. While this is a warning and your app will still run, ignoring it can lead to unexpected behavior, especially in dynamic lists where items can be added, removed, or reordered. The warning indicates that React is falling back to using array indexes internally, which can cause problems when the list order changes.
Open your browser's developer console and examine the warning message. React usually provides a stack trace pointing to the component file and line number where the list is being rendered.
Look for code patterns like:
// Missing key prop
items.map(item => <ListItem />)
// Or with fragments
items.map(item => <><div>...</div></>)The warning typically appears in components that use .map(), .filter(), or other array methods to render multiple elements.
The best solution is to use unique identifiers that already exist in your data, such as database IDs or UUIDs. These IDs should be stable (not change between renders) and unique within the list.
// Good: Using stable database IDs
const users = [
{ id: 'user-123', name: 'Alice' },
{ id: 'user-456', name: 'Bob' }
];
users.map(user => (
<UserCard key={user.id} user={user} />
))
// Good: Using unique slugs or identifiers
products.map(product => (
<ProductItem key={product.sku} product={product} />
))The key should be passed to the outermost element or component returned by the map function, not to a child element inside it.
If your data doesn't have natural unique identifiers AND the list is static (never reordered, filtered, or items added/removed), you can use array indexes as a last resort.
// Acceptable only if list never changes
const staticItems = ['Home', 'About', 'Contact'];
staticItems.map((item, index) => (
<NavLink key={index}>{item}</NavLink>
))ā ļø Do NOT use indexes if:
- Items can be reordered (sorting, drag-and-drop)
- Items can be added or removed (filtering, deleting)
- The list is paginated or infinite-scrolling
- List items contain stateful components or form inputs
Using indexes in these scenarios will cause React to reuse component instances incorrectly, leading to bugs.
If your data doesn't have unique identifiers, generate them when the data is created, not during rendering.
import { v4 as uuidv4 } from 'uuid';
// Good: Generate IDs when creating data
const [items, setItems] = useState([
{ id: uuidv4(), text: 'Item 1' },
{ id: uuidv4(), text: 'Item 2' }
]);
// When adding new items
const addItem = (text) => {
setItems([...items, { id: uuidv4(), text }]);
};
// Render with stable IDs
items.map(item => (
<ListItem key={item.id} item={item} />
))Alternatively, use crypto.randomUUID() (built into modern browsers) or a counter:
let nextId = 0;
const [items, setItems] = useState([
{ id: nextId++, text: 'Item 1' },
{ id: nextId++, text: 'Item 2' }
]);Never generate keys during render (like key={Math.random()}) as this defeats the purpose entirely.
The key prop must be on the element returned directly by the map function, not on a child element. This is a common mistake when extracting list item components.
// Wrong: Key on wrong element
items.map(item => (
<ListItem>
<div key={item.id}>{item.name}</div>
</ListItem>
))
// Wrong: Key inside the component
function ListItem({ item }) {
return <li key={item.id}>{item.name}</li>; // Key should be on <ListItem />
}
// Correct: Key on the mapped element
items.map(item => (
<ListItem key={item.id} item={item} />
))
function ListItem({ item }) {
return <li>{item.name}</li>; // No key here
}For React Fragments, use the explicit syntax to add keys:
items.map(item => (
<React.Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.definition}</dd>
</React.Fragment>
))Keys only need to be unique among siblings in the same array, not globally unique across your entire app.
// Correct: Keys unique within each list
<div>
<ul>
{fruits.map(fruit => <li key={fruit.id}>{fruit.name}</li>)}
</ul>
<ul>
{vegetables.map(veg => <li key={veg.id}>{veg.name}</li>)}
</ul>
</div>However, if you're concatenating multiple arrays or have nested lists, ensure keys don't collide:
// Problem: IDs might overlap between arrays
const allItems = [...userItems, ...adminItems];
// Solution: Prefix keys to ensure uniqueness
[
...userItems.map(item => <Item key={`user-${item.id}`} item={item} />),
...adminItems.map(item => <Item key={`admin-${item.id}`} item={item} />)
]After adding keys, refresh your application and check that the console warning no longer appears. Test the list's behavior:
# Clear browser cache if needed
# Open DevTools Console (F12)
# Look for warnings in red textTest dynamic operations to ensure keys work correctly:
- Add new items to the list
- Remove items
- Reorder items (if applicable)
- Filter the list
- Check that form inputs maintain focus and state
- Verify animations work smoothly
If the warning persists, double-check that the key prop is on the correct element and that you're not accidentally overriding it elsewhere in your code.
Why Keys Matter for Performance:
React uses a "reconciliation" algorithm to determine what changes need to be made to the DOM. Keys tell React which array element each component corresponds to, so React can match them up between renders. Without keys, React can't tell if you've reordered items or replaced them, so it may destroy and recreate DOM nodes unnecessarily, losing component state in the process.
Keys and Component State:
When a component's key changes, React treats it as a completely different component and unmounts the old instance (destroying its state) and mounts a new one. This is actually a useful feature - you can intentionally change a component's key to force it to reset. For example, <UserProfile key={userId} /> will reset its state when userId changes.
Server-Side Rendering (SSR):
In Next.js and other SSR frameworks, key warnings may appear during hydration if server and client render different lists. Ensure your data fetching is consistent between server and client, or use suppressHydrationWarning carefully.
React DevTools:
Install React DevTools browser extension to inspect which components are re-rendering when list updates. Components with missing or incorrect keys will show excessive re-renders.
TypeScript:
If you're using TypeScript, ensure your key prop types allow the values you're passing. Keys should be strings or numbers, not objects or complex types.
Performance Monitoring:
For large lists (100+ items), consider virtualization libraries like react-window or react-virtuoso, which render only visible items. Proper keys become even more critical with virtualization.
Common Anti-Patterns:
- Don't use object references as keys: key={item} (use key={item.id})
- Don't concatenate values that might not be unique: key={${item.category}-${item.name}} unless you're certain it's unique
- Don't omit keys thinking "my list never changes" - requirements change, and future developers may not know your assumption
Related ESLint Rules:
Enable "react/jsx-key" rule in your ESLint config to catch missing keys during development. Configure it to enforce keys in array literals and iterators.
Prop spreading could cause security issues
Prop spreading could cause security issues
Error: error:0308010C:digital envelope routines::unsupported
Error: error:0308010C:digital envelope routines::unsupported
React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render.
React Hook useEffect placed inside a condition
Hook can only be called inside the body of a function component
Hook can only be called inside the body of a function component
Rollup failed to resolve import during build
How to fix "Rollup failed to resolve import" in React