In React, how do you render an array of items in JSX, and why does React log the warning "Each child in a list should have a unique key prop"?
answer
- arrays are valid JSX children
- identity, not position
- survives insert, delete, sort
- stable id from the data
- warning means: I assumed position
basics
~20 sReact renders a list by putting an array of elements in a JSX slot, normally items.map(item => <Row key={item.id} />). The key prop gives each element an identity independent of its position, so React can match it to the same item on the next render.
solid answer
~50 sA JSX expression slot accepts an array, so the idiom is `{items.map(item => <Row key={item.id} item={item} />)}` — `map` returns an array of elements and React renders them in order. On every render React produces a fresh set of children and has to decide which new child corresponds to which old one; that pairing is what preserves a DOM node, its focus and scroll position, and the component instance's state. For statically written children, position is enough. For an array it is not — items get inserted, filtered and reordered — so `key` supplies the identity instead. Without keys React falls back to matching by index and warns, which is really React saying "I have no identity for these children, so I will assume position is identity." The right key is a stable id from the data, unique among its siblings and the same on every render for the same item.
code
jsx · 11 linesexport function PostList({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
{post.title} — {post.author}
</li>
))}
</ul>
);
}go deeper
Be able to write the map-with-key idiom from memory and say plainly that the key should be a stable id from the data, not the array index.
Explain the mechanism: React pairs new children with old ones, and the key is what makes that pairing survive insertion, removal and sorting instead of following position.
Show what the pairing actually preserves in production — component state, uncontrolled input values, focus, scroll and in-flight transitions — and be ready to name a real bug you traced back to bad keys.
Frame keys as an identity contract on your data: decide where item ids are minted, keep them stable for the item's whole lifetime, and treat "this list has no natural id" as a data-modelling gap rather than a rendering detail.
## Rendering an array in JSX A JSX expression slot `{...}` can hold a single element, a string, a number, `null`, or an **array** of elements, and React renders an array by rendering each entry in order. That is why the standard list idiom is `Array.prototype.map`: ```jsx function PostList({ posts }) { return ( <ul> {posts.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> ); } ``` `map` returns a new array of React elements and React splices that array into the children of `<ul>`. Nothing else is required — no loop helper, no wrapper element, no manual concatenation. ## What `key` is actually for A React element is a plain description of what the UI should look like, and the parent produces a brand-new set of them on every render. To *update* the screen instead of rebuilding it, React must decide which element in the new children corresponds to which element in the previous children. That pairing is what keeps things alive across renders: the existing DOM node, the text a user typed into an uncontrolled input, the focus ring, the scroll offset, a running CSS transition, and the state held inside the child component instance. For children written literally in JSX, position answers the question — the second child is always the second child. For a dynamic array it does not: rows are inserted, removed, filtered and sorted, so position 2 in the new render may be a completely different item than position 2 in the old one. `key` gives each child an identity that does not depend on position: - same key in both renders → the same conceptual item, so React updates that existing instance and DOM node in place; - key present before but absent now → the item is gone, so React unmounts it and removes its DOM; - key present only now → a new item, mounted fresh. When you omit keys, React falls back to matching children by index and logs `Warning: Each child in a list should have a unique "key" prop.` The warning is not a style nag — it is React telling you it will treat position as identity. ## Choosing a key In order of preference: 1. **A stable id that already exists in the data** — database primary key, UUID, slug, SKU. This is the common case and the expected answer. 2. **A natural business key that cannot collide** — an email address for a user row, an ISO date for a per-day row. 3. **A composite of stable fields** when no single field is unique, e.g. `` key={`${row.userId}-${row.date}`} ``. 4. **An id you generate once when the item is created** and store alongside it — created in the reducer or normalisation step, never during render. The key must be a string or a number, unique **among its siblings**, and identical across renders for the same item. It must never be `Math.random()` or `crypto.randomUUID()` evaluated inline in the JSX: those mint a fresh key on every render, so React concludes every item was replaced and remounts the whole list. ## Where the key goes The key belongs on the outermost element produced per iteration — the element that is directly an entry of the array: ```jsx {rows.map((row) => ( <Row key={row.id} row={row} /> ))} ``` It does not go on an element inside `Row`'s own returned markup; React reads the key where the array entry sits, in the parent that built the list. ## Things that go wrong - **Suppressing the warning.** It is the only signal that React has no identity for those children, and the resulting bugs surface as "the wrong row is edited" rather than as an error. - **Duplicate keys inside one list.** React warns `Encountered two children with the same key` and the matching becomes ambiguous, so state can appear to jump between rows or an item can silently vanish. - **Expecting to read the key in the child.** `key` is consumed by React and is not forwarded in `props`; pass the id again as an ordinary prop if the child needs it. - **Reaching for the array index by default.** It works only while the list never changes shape, and fails the moment anything is inserted, removed or sorted.
- What happens if two items in the same list end up with the same key?React logs `Encountered two children with the same key` and the pairing between old and new children becomes ambiguous. In practice one of the duplicates can be dropped, or state and DOM nodes appear to swap between the two rows. Deduplicate at the data layer, or build a composite key from fields that together are unique, rather than hoping the collision is harmless.
- Does adding keys make rendering the list faster?Not by itself. Keys make updates *correct* by telling React which existing instance matches which item; the speed benefit is a side effect — React can move and reuse nodes instead of tearing them down and rebuilding them. A list with correct keys still re-renders every visible row when the parent re-renders; keys are not a memoization mechanism.
- Where exactly do you put the key when each row renders a component rather than a DOM element?On the component element itself in the parent's array — `{rows.map(row => <Row key={row.id} row={row} />)}` — because that element is the array entry React is matching. Putting it on markup inside `Row`'s own return value does nothing for the list, and React will still warn that the array children have no keys.
saying these in an interview costs you the question
- The key just silences a console warning.
- Any unique value works, so Math.random() is fine.
- Keys must be unique across the entire application.
- The child component reads its own key from props.
- Keys exist to make React render the list faster.