Each child in a list should have a unique key prop
3 min read
As a React developer, you've surely seen the following error message.
Warning: Each child in a list should have a unique "key" prop.
This error message is reported in React when we map over data without generating keys.
Let's take a look at an example of a Products
component that throws this error.
type Props = {
products: string[];
}
const Products = ({ products }: Props) => {
return (
<ul>
{products.map((product) => {
return (
<li>{product}</li>
);
})}
</ul>
);
}
export { Products };
The above example throws the error because there is no key
set on the <li>
element when mapping over the products
array.
Whenever we want to render an array of items in React, we must provide keys for each item in order to help React identify each item. Each key must be unique.
Most online tutorials will tell you to use the array index as the key for each item in order to solve the problem. Here is what that would look like.
const Products = ({ products }: Props) => {
return (
<ul>
{products.map((product, index) => {
return (
<li key={index}>{product}</li>
);
})}
</ul>
);
}
This is not a good idea. Using an item's index
as its key can cause problems when the order of items rendered changes over time. If items are added, deleted, or reordered, strange bugs can occur.
It's better to solve this problem in a way that is always guaranteed to be safe, without the risk of surprise bugs.
If our array does not have a stable id
per product that comes from a database, we can use the crypto.randomUUID()
method.
The crypto.randomUUID()
method is provided by major web browser to generate unique keys for items. This method will generate a randomly generated, 36 character long unique string. This will guarantee that each item in the list gets a unique ID.
Be careful! We should not use crypto.randomUUID()
to generate IDs on the fly.
// Do not do this!
<li key={crypto.randomUUID()}>
{product}
</li>
Generating the ID directly in the JSX that is in the map()
will make the key
change on every component render. Every time the key
changes, the corresponding element will be recreated by React. This is slow, affects performance, and will cause any user input inside list items to be lost.
Remember that the products
array that we are dealing with is just an array of product names, without any id
associated to them. Let's take a look at how we can use crypto.randomUUID()
properly to create a productsWithIds
array from the initial products
array.
const Products = ({ products }: Props) => {
const productsWithIds = products.map(product => {
return {
name: product,
id: crypto.randomUUID(),
};
});
return (
<ul>
{productsWithIds.map(({ id, name }) => {
return (
<li key={id}>{name}</li>
);
})}
</ul>
);
}
Let's now test out these latest changes to our Products
component.
export default function App() {
return (
<div>
<h1>Products</h1>
<Products products={['Switch', 'XBOX', 'PS5']} />
</div>
);
}
We'll notice that there is no more React error message about non-unique keys being used in our list. Even though no unique IDs were provided by the products
array, we made sure to generate unique keys that are both safe and stable.