Skip to content

React Tutorial Props and Data

Understanding React Keys: Why Lists Need Them

Lesson 40 of 156 3 min read By Deepak Paswan Updated Oct 10, 2026

Runnable lesson · press Try it on the examples to edit and run them here.

When you render a list, React asks for a key on each item. If you leave it out, the console shows a warning. This lesson explains what keys do and how to choose them.

What a key is for

When data changes, React compares the old list with the new list. It must work out which item is which: which one moved, which was added and which was removed. A key is the identity of an item. React matches items with the same key between renders, and keeps their DOM nodes and their state.

Without keys, React can only compare by position, and position changes the moment you insert, delete or reorder.

Choose a key that is unique and stable

  • Unique among siblings. Keys only need to be unique inside the same array, not across the whole app.
  • Stable. The key of an item must stay the same each time it renders.
  • Taken from the data. Use a database id, a slug or an id you generate when you create the item.
App.jsx
JSX
function App() {
  const items = [
    { id: "a1", name: "Milk" },
    { id: "b2", name: "Eggs" },
  ];
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

If your data has no id, create one when the item is created, for example with crypto.randomUUID() in the event handler. Do not generate it during rendering, because then it changes on every render.

What goes wrong without keys

Look at the missing-key warning. Try this in the playground and open the console panel under the preview:

App.jsx
JSX
function App() {
  const items = ["Milk", "Eggs"];
  return (
    <ul>
      {items.map((item) => (
        <li>{item}</li>
      ))}
    </ul>
  );
}

React 19 shows a message that each child in a list should have a unique key prop, and it names the component to look at.

Why the array index is a bad key

The index is available (map((item, index) => ...)), and it silences the warning. It works only for lists that never change order, never get inserted into and never lose items. Otherwise the index points at a different item after the change, and things get mixed up. Here is the bug live. Type something into the first box of each list, then press Remove first item in each.

App.jsx
JSX
function Row({ label, boxLabel }) {
  return (
    <li>
      {label} <input aria-label={boxLabel} />
    </li>
  );
}

function App() {
  const [fruits, setFruits] = React.useState([
    { id: 1, name: "Apple" },
    { id: 2, name: "Banana" },
    { id: 3, name: "Cherry" },
  ]);
  const [fruits2, setFruits2] = React.useState(fruits);

  return (
    <div>
      <h3>Index keys</h3>
      <ul>
        {fruits.map((f, i) => (
          <Row key={i} label={f.name} boxLabel={"Index box " + (i + 1)} />
        ))}
      </ul>
      <button onClick={() => setFruits(fruits.slice(1))}>Remove first (index keys)</button>

      <h3>Id keys</h3>
      <ul>
        {fruits2.map((f) => (
          <Row key={f.id} label={f.name} boxLabel={"Id box " + f.name} />
        ))}
      </ul>
      <button onClick={() => setFruits2(fruits2.slice(1))}>Remove first (id keys)</button>
    </div>
  );
}

Look at the result. In the index list, the text you typed beside “Apple” is still in the first box after you remove Apple, so it now sits next to “Banana”. React matched items by position: the first row stayed, and only its label changed. In the id list, the typed text disappears together with its row, which is what you expect.

Keys do not belong in props

key is read by React and is not passed to your component. If a child needs the id, pass it as a separate prop (id={item.id}).

Where the key goes

The key goes on the outermost element returned by map:

App.jsx
JSX
{items.map((item) => (
  <Row key={item.id} item={item} />   // key on the element map returns
))}

Not on an element inside Row. For several elements per item, use <Fragment key=...>, see Fragments.

Keys can reset state on purpose

Changing a component’s key makes React treat it as a new component, discarding the old state. That is a clean way to reset a form when the item being edited changes: <EditForm key={user.id} user={user} />.

Key points

  • A key is an item’s identity; React uses it to match items between renders.
  • Use unique, stable ids from your data. Avoid the array index when the list can change.
  • Put the key on the element returned by map, and remember it is not a prop.