Lesson 4 of 31
Rendering Lists and Keys
Turning an array of data into an array of elements with map, why every item needs a key, and why the wrong choice of key causes subtle bugs rather than crashes.
Most real UI is a list of something: search results, comments, rows in a table. React doesn't have a special looping tag for this — you reach for a plain JavaScript array method.
.map(), not a loop
JSX only accepts expressions inside {}, and a for loop is a statement,
not an expression — so it can't go directly inside JSX. .map() is the
natural fit instead, because it's an expression that returns a new array,
and React knows how to render an array of elements:
function TrackList({ tracks }) {
return (
<ul>
{tracks.map((track) => (
<li key={track.id}>{track.title}</li>
))}
</ul>
);
}
<TrackList tracks={[
{ id: "js", title: "JavaScript" },
{ id: "ts", title: "TypeScript" },
{ id: "react", title: "React" },
]} />Each call to the arrow function returns one <li> element; .map() collects
them into an array; React renders that array as siblings.
Why every item needs a key
You'll notice the key={track.id} above. Skip it, and React logs a console
warning — but more importantly, without a key React has no way to tell
which rendered element corresponds to which piece of data once the list
changes. Its fallback, matching by position, causes real bugs whenever items
are added, removed, or reordered anywhere but the end of the list.
Here's the concrete failure. Imagine a list of text inputs, one per todo item, keyed by index:
{todos.map((todo, index) => (
<input key={index} defaultValue={todo.text} />
))}If the user types into the second input, then the first todo is deleted, React sees "same position, index 0" for what is now a different todo — and because this is an uncontrolled input holding its own DOM state, the typed text stays attached to position 0 rather than following the todo it belongs to. The text appears to jump to the wrong row. This isn't a rare edge case; it's the single most common React list bug, and it produces no error message at all — just quietly wrong behavior.
What actually makes a good key
A key should be:
- Stable — the same value across re-renders for the same logical item (a database id, not
Math.random()recomputed every render). - Unique among siblings in that list (not globally unique — the same key can reappear in a different list).
An item's own id is almost always the right choice:
{tracks.map((track) => <li key={track.id}>{track.title}</li>)}When index is actually fine
If a list is genuinely static — never reordered, nothing inserted or removed except possibly at the very end, no per-item local state like an uncontrolled input — index and identity coincide, so index works:
{["Mon", "Tue", "Wed", "Thu", "Fri"].map((day, index) => (
<span key={index}>{day}</span>
))}The days of the week never reorder, so this is safe. The rule isn't "index is always wrong" — it's "index is only as safe as the list is static," and most real, interactive lists aren't.
Try it yourself
This list is keyed correctly by id. Try changing key={track.id} to
key={index} and adding a console.log — with a static array like this one
nothing visibly breaks, which is exactly why the bug is easy to miss until a
list becomes dynamic (sortable, filterable, or editable).
What to remember
- Use
.map()to turn an array of data into an array of JSX elements — JSX has no built-in loop tag. - Every item in a rendered list needs a
keyso React can match elements to data across re-renders. - Prefer a stable, unique id from the data itself; array index only works safely for lists that never reorder or have items inserted/removed except at the end.
- A missing or duplicate key logs a warning, not a crash — which is exactly why the resulting bugs are easy to ship unnoticed.
Check yourself
4 questions · pass 3/4 to unlock Conditional Rendering
1.What is the standard way to render an array of data as a list of elements in React?
2.Why does React want a stable
keyprop on each item in a rendered list?3.When is using an item's array index as its
keyacceptable?4.What does React do if two sibling elements in a list share the same
key?
4 left to answer