iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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…