Skip to content
WANCODE
BM

Lists and Keys

18 minBeginnerReact

In this lesson

  • Render an array of data as elements with array.map()
  • Give each item a unique, stable key and explain what React uses it for
  • Recognise the missing-key warning and the bugs caused by key={index}

The big idea

Lists in React are just arrays mapped to elements. The key is each item's name tag — it lets React tell items apart between renders, so use something that belongs to the item, like its id.

Menus, chat messages, search results — most UIs show lists of data. You almost never write each <li> by hand. Instead, you keep the data in an array and use map() to turn each item into an element.

JSX
const drinks = [
  { id: "d1", name: "Teh Tarik" },
  { id: "d2", name: "Milo Ais" },
];

<ul>
  {drinks.map((drink) => (
    <li key={drink.id}>{drink.name}</li>
  ))}
</ul>

JSX can render an array of elements directly, so drinks.map(…) inside {} just works. The new part is key.

Why does React need keys?

When the list changes (an item is added, removed or re-ordered), React compares the new list with the old one. The key tells React which item is which, so it can keep each item's DOM and state attached to the right data — instead of guessing by position.

key
A special prop on the element returned from map(). It must be unique among its siblings and stay the same for the same item across renders. Your component can't read it as a prop.
Stable id
An identifier that belongs to the data itself — a database id, a product code, a slug. The best key.

The warning you'll see

Forget the key and React's development build prints: Warning: Each child in a list should have a unique "key" prop. Watch for it in the console below the preview — it's telling you about a real bug waiting to happen.

Index as key: the quiet bug

map((item, index) => <li key={index}>…) silences the warning, but the key now means "position 0, position 1…". Remove the first item and every item shifts up a position — React thinks the last item was removed and re-uses the other rows for different data. Anything kept inside those rows (typed text, checkbox state, component state) ends up next to the wrong item.

Breaks when the list changes

JSX
{dishes.map((dish, index) => (
  <li key={index}>{dish.name}</li>
))}

Stable — follows the data

JSX
{dishes.map((dish) => (
  <li key={dish.id}>{dish.name}</li>
))}
  • Use an id from your data. If you create items yourself, give them an id when you create them.
  • Index is acceptable only for lists that never re-order, filter, or have items added/removed in the middle.
  • Never use Math.random() — a new key every render makes React throw away and rebuild every row each time.

Replace the single hard-coded <li> with dishes.map(…) so all four dishes appear. Keep the same markup for each row (li.dish with .name, .price and the .qty input), show the price with two decimals using dish.price.toFixed(2), and give each <li> a key from the data — the console must show no key warning.

Checks

  • All four dishes are listed
  • Prices show two decimals
  • No missing-key warning in the console

Check understanding

Q1

Where does the key go?

Q2

What's wrong with key={Math.random()}?

Q3

When is key={index} acceptable?

What you learned

  • array.map(item => <li key={item.id}>…</li>) turns data into UI.
  • Keys let React match items between renders; they must be unique among siblings and stable.
  • Missing keys trigger a console warning; key={index} hides the warning but mixes up state when the list changes.
Preview

Console output from your code appears here.

Step 1 of 2