Section 2 · Components & Props
Rendering Lists with Keys
22 min3 min readCode playground
Lesson at a glance
Render lists from arrays with map, filter and sort them without mutating data, and choose keys that keep each item’s identity and state correct between renders.
No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.
The Watchlist is a list. So is a feed, a table of orders, a set of search results, a menu. Most UI is lists of things built from arrays of data, and React’s approach is the one you’d guess from JavaScript: transform the array of data into an array of elements.
From data to elements with map
export default function MovieList({ movies }) {
return (
<ul>
{movies.map((movie) => (
<li key={movie.id}>
{movie.title} ({movie.year})
</li>
))}
</ul>
);
}map returns an array of <li> elements, and React renders arrays by rendering each item in order. There’s no special loop syntax to learn; it’s the array method you already know.
Filtering and sorting are the same idea: prepare the array, then map it. Do it during render, from the data you have. The visible list is derived, so it doesn’t need to be state.
const movies = [
{ id: 'a1', title: 'Arrival', year: 2016, watched: true },
{ id: 'p2', title: 'Past Lives', year: 2023, watched: false },
{ id: 'd3', title: 'Dune: Part Two', year: 2024, watched: false },
];
const toWatch = movies
.filter((m) => !m.watched)
.toSorted((a, b) => b.year - a.year);
console.log(toWatch.map((m) => m.title)); // ["Dune: Part Two", "Past Lives"]
console.log(movies[0].title); // Arrival (the original order is untouched)toSorted returns a sorted copy and is supported in every current browser. The older sort reorders the array in place. Called on state or props, that silently mutates data you don’t own. If you need to support older browsers, copy first: [...movies].sort(…).
What keys are for
Every element in a list needs a key prop, and React warns in the console when one is missing. The reason becomes clear when the list changes.
When React re-renders a list, it has the old array of elements and the new one. It needs to match them up: which new element is the same item as which old one? That matters because each item may have state, such as a half-typed note in an input, a focused button or an open menu, and that state must stay with the right item.
The key is the item’s name tag. React matches old and new elements by key. Same key: it’s the same item, so React keeps its DOM and state and updates what changed. New key: a new item, so React creates it. Missing key: the item was removed, so React destroys it.
Choosing a key
A good key is unique among siblings and stable: the same item has the same key on every render.
- Use an id from your data. Database rows and API objects already have one.
- Create ids when items are created. For movies added through a form, generate the id in the handler that adds them:
{ id: crypto.randomUUID(), title, year }. It’s stored on the object and never changes. - Never generate keys during render.
key={Math.random()}orkey={crypto.randomUUID()}insidemapproduces new keys every render. React treats every row as brand new, tears down the DOM and loses input focus and state each time you type. - Don’t use content that isn’t unique. Titles repeat; there are several films called “Dune”.
Keys only need to be unique among siblings in the same list. Two different lists can use the same ids.
Keys on components and fragments
The key goes on the outermost element returned from the map callback. When that’s a component, put it on the component:
{movies.map((movie) => (
<MovieItem key={movie.id} movie={movie} />
))}MovieItem can’t read key; React uses it and doesn’t pass it down. If the component needs the id, pass it separately as a prop.
When each item renders several siblings without a wrapper, use the long form of a fragment, which accepts a key:
import { Fragment } from 'react';
{movies.map((movie) => (
<Fragment key={movie.id}>
<dt>{movie.title}</dt>
<dd>{movie.year}</dd>
</Fragment>
))}The short <>…</> syntax can’t take a key.
You can now render any list correctly. That completes the static Watchlist. In the next section, it starts to move: state lets the list change when the user clicks, types and submits.
Key takeaways
- Turn an array of data into an array of elements with
map, and put thekeyon the outermost element the callback returns. - A key tells React which item is which between renders, so it must be unique among siblings and stable over time.
- Use an id from your data as the key; create ids when items are created, never during render.
- Index keys break when the list can be reordered, filtered or inserted into, because state sticks to the position instead of the item.
- Filter and sort copies of your data while rendering;
sortchanges the array in place, so usetoSortedor sort a copy.