To render a list in React, call map() on an array and return a piece of JSX for each item. Give the outermost element of each item a key prop with a value that identifies that item, usually its ID, so React can tell the items apart when the list changes.
Add { id: 'dat', name: 'Date', price: 4 } to the array and a fourth line appears. JSX does not have a loop syntax of its own: map returns an array of elements, and React renders an array by rendering each element in order.
Rendering arrays with map
map calls your function once per item and collects the results into a new array. Inside JSX braces that array is just a value, so you can map straight in the markup, as above, or build the array first:
const items = fruits.map((fruit) => <li key={fruit.id}>{fruit.name}</li>);
return <ul>{items}</ul>;
Two details trip people up. If you use braces in the arrow function, you need a return: fruits.map((f) => { return <li key={f.id}>{f.name}</li>; }). Without it the function returns undefined and the list is empty. And the key goes on the element returned from map, not on an element inside it. If the item is a component, put the key on the component: <FruitRow key={fruit.id} fruit={fruit} />.
Filter, then map
To show some of the items, filter the array first and map the result. Each method does one job, and the chain reads like the sentence "the fruits that match, as list items".
Type er in the search box and the list narrows to Eraser, Ruler and Stapler; tick In stock only and Ruler goes too. The filtered list is computed during render from the two pieces of state, so there is no second array in state to keep in sync. visible.length === 0 && is a safe use of && because the comparison is a real boolean (the conditional rendering page shows what happens with a bare 0).
What keys do
When a list re-renders, React compares the new array of elements with the previous one. Keys are how it matches them: the element with key 3 now is the element with key 3 before, even if it moved. React keeps that item's DOM node and its component state, and only moves or updates what changed. Items with new keys are created, and keys that vanished are removed.
Without keys, the only thing React can match by is position. That is fine while nothing moves, and wrong as soon as something does.
Why index keys break
map passes the index as its second argument, so key={index} is tempting. The index describes a position, not an item. Insert an item at the top and every item's index shifts by one, so React believes item 0 is still item 0 and keeps its old state there.
The demo below renders the same list twice, once keyed by index and once by ID. Each row has an input that keeps its own text.
Type math next to Ada in both lists, then click Add to top. In the ID list the note stays with Ada. In the index list the note jumps to the new person, because the first input is still the input at position 0, and position 0 is now someone else. The names are right in both lists, since they come from the data; only the state that lives in the DOM and in components goes wrong. Real apps hit this with checkboxes, focus, animations and any component that holds useState.
Index keys are safe only when all of these hold: the list is never reordered or filtered, items are never inserted or removed except at the end, and the items have no state of their own. A static list of footer links qualifies. Anything a user can edit usually does not.
Where keys come from
The best key is an ID that is already in the data: a database ID, a product SKU, a slug, a username. Data from a server almost always has one.
For items created in the browser, give each item an ID when you create it and store it with the item: a counter, as in the demo above, or crypto.randomUUID(). Never generate the key during render:
// Wrong: a new key on every render, so React remounts every item every time
{todos.map((todo) => <Todo key={Math.random()} todo={todo} />)}
// Right: the ID is created once, in the event that adds the item
function addTodo(text) {
setTodos([...todos, { id: crypto.randomUUID(), text }]);
}
A key that changes on every render is worse than an index: every item is destroyed and recreated each time, inputs lose focus while you type, and nothing keeps its state. The same trick is useful on purpose: changing a component's key resets it, which the useState page uses to clear a form.
Keys are unique among siblings only
A key has to be unique within one list, not across the app. Two separate lists can reuse the same IDs, because React only compares keys among the children of the same parent.
Both sections have dishes with keys 1 and 2, and that is correct: each ul is its own list. Duplicate keys inside one list are the problem: React cannot tell the clashing items apart, and when the list updates it may duplicate or drop them.
When one item renders several sibling elements, such as a <dt> and a <dd>, wrap them in <Fragment key={item.id}>. The short <> syntax cannot take a key.
The "unique key prop" warning
If you leave out the key, React prints this in the browser console during development:
Each child in a list should have a unique "key" prop.
Check the render method of `App`.
The second line names the component whose render produced the list, which tells you which map to fix. Two siblings with the same key get a different message, starting "Encountered two children with the same key". The preview on this page runs like a production build, which skips that check, so you will not see the warning here even if you delete a key. Delete one anyway in the first example: the list still renders, because a missing key is a correctness risk, not a crash. To fix the warning, add a stable key to the element returned from map; adding key={index} silences it but brings back the bug shown above.
Also note that key is not passed to your component. Inside FruitRow, props.key is undefined. If the component needs the ID, pass it a second time as a normal prop: <FruitRow key={f.id} id={f.id} />.
Frequently Asked Questions
Why does React need a key on list items?
Keys tell React which item is which between renders. When the array changes, React matches old and new items by key, so it can keep each item's state and DOM node with the right data instead of guessing by position.
Can I use the array index as a key?
Only for a list that never changes order and never has items inserted or removed in the middle. If items can move, an index key ties state to the position, so typed text, checkboxes and focus end up on the wrong item.
What should I use as a key?
An ID that belongs to the data: a database ID, a product SKU, a username. For items created in the browser, generate an ID once when you create the item (a counter or crypto.randomUUID()), never during render.
Do keys have to be unique across the whole app?
No. Keys only need to be unique among siblings in the same list. Two different lists can use the same keys.
Can a component read its own key?
No. key is used by React and is not passed to the component as a prop. If the component needs the ID, pass it again under another name, such as id={item.id}.
How do I fix "Each child in a list should have a unique key prop"?
Add a key to the outermost element you return from map, using a stable ID from the item. If that element is a fragment, use <Fragment key={id}>, since <> cannot take a key.