Skip to main content

Rendering Lists

Rendering a list is just .map() turning data into elements. The interesting part isn't the map — it's key, and why React demands it.

Map data to elements

An array of elements drops straight into JSX, because React renders each item in an array.

ProductList.jsx
function ProductList({ products }) {
return (
<ul>
{products.map((p) => (
<li key={p.id}>{p.name}</li>
))}
</ul>
);
}

Filter first, then map

No special syntax for "only some" — I filter the array, then map what's left. Both are just JS.

ActiveUsers.jsx
{users
.filter((u) => u.isActive)
.map((u) => <UserRow key={u.id} user={u} />)}

Keys — what they're actually for

A key isn't decoration and it isn't for me — it's how React identifies which item is which across renders. When the list changes, React matches old elements to new ones by key, so it knows what moved, what's new, and what to leave alone. Get the key right and React reuses DOM nodes and component state correctly; get it wrong and it quietly mixes them up.

Don't use the array index as a key when the list can reorder, filter, or have items inserted. The index describes a position, not an item — so when things shift, React thinks item #2 is still item #2 and hands the wrong state (input values, focus, animation) to the wrong row.

key-trap.jsx
{items.map((item, i) => <Row key={i} item={item} />)}    // reorders → state leaks between rows
{items.map((item) => <Row key={item.id} item={item} />)} // stable identity, correct

Index is only fine for a list that never changes order and never grows or shrinks.

A key has to be stable (same item → same key every render) and unique among siblings. That's it — it doesn't need to be globally unique, and I never generate it inside render (key={Math.random()} gives a new key every time, so React rebuilds every row from scratch).

When each item needs multiple tags

If a single item renders more than one node, I can't put the key on a wrapping <div> I don't want — I use a <Fragment> with the key. The <>…</> shorthand can't take a key, so this is the one place I write it out.

Glossary.jsx
import { Fragment } from "react";

{terms.map((t) => (
<Fragment key={t.id}>
<dt>{t.name}</dt>
<dd>{t.definition}</dd>
</Fragment>
))}