Each child in a list should have a unique key prop

March 10, 2023React

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.

Products.tsx
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.

Products.tsx
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.

Products.tsx
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.

App.tsx
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.