Lists & Keys
Rendering lists in React: .map, the all-important key prop, virtualisation for big data, and the patterns that scale.
React — rendering lists
EXAMPLE
// ===== The basic shape =====
function UserList({ users }) {
return (
<ul>
{users.map(u => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}
// ===== The key prop =====
// Must be UNIQUE among SIBLINGS, STABLE across renders.
// Bad: index as key when the list mutates (insert/delete/reorder).
{items.map((it, i) => <Item key={i} {...it} />)}
// Good: stable IDs from the data.
{items.map(it => <Item key={it.id} {...it} />)}
// If you truly have no id: use a hash or a useMemo'd uuid attached to the item once.
// ===== Empty state =====
function UserList2({ users }) {
if (users.length === 0) return <p>No users yet.</p>;
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
// ===== Fragment with keys =====
import { Fragment } from 'react';
function Glossary({ entries }) {
return (
<dl>
{entries.map(e => (
<Fragment key={e.term}>
<dt>{e.term}</dt>
<dd>{e.definition}</dd>
</Fragment>
))}
</dl>
);
}
// ===== Sorting + filtering in render =====
function Table({ rows, query, sortKey }) {
const filtered = rows
.filter(r => r.name.toLowerCase().includes(query.toLowerCase()))
.sort((a, b) => (a[sortKey] > b[sortKey] ? 1 : -1));
return <ul>{filtered.map(r => <li key={r.id}>{r.name}</li>)}</ul>;
}
// For large lists, useMemo on the derived array.
// ===== Virtualised lists (large data) =====
// react-window or @tanstack/react-virtual render only visible rows.
import { FixedSizeList } from 'react-window';
function BigList({ items }) {
return (
<FixedSizeList height={400} itemCount={items.length} itemSize={32} width={300}>
{({ index, style }) => (
<div style={style} key={items[index].id}>{items[index].name}</div>
)}
</FixedSizeList>
);
}
// ===== Patterns to internalise =====
// - Always pass key with a stable, unique id
// - Empty states explicit, not 'null when length is 0'
// - Memoise expensive derivations (filter + sort)
// - Virtualise once a list exceeds ~200 visible rows
// ===== Pitfalls =====
// - Index as key on a reordering list -> wrong DOM reuse + lost focus
// - Inline key={Math.random()} -> remounts every render
// - filter/sort in render without useMemo on big lists
// - Skipping virtualisation on long lists -> sluggish scroll
Why it matters
Lists are .map plus a stable key. Empty states explicit, derivations memoised, virtualisation when the count gets serious. Get the key right and React diffs correctly; get it wrong and every reorder is a footgun.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
const items = ['apple', 'banana'];
return <ul>{items.map(i => <li key={i}>{i}</li>)}</ul>;
Try it Yourself »
Discussion
Loading…