Rendering lists and keys

React · Essentials · 12 min

Lists are everywhere, and wrong keys cause subtle bugs where state sticks to the wrong row.

To render a list, transform your data array into an array of JSX elements, usually with map, and filter first with filter if needed:

const items = people
  .filter(p => p.active)
  .map(p => <li key={p.id}>{p.name}</li>);
return <ul>{items}</ul>;

Every element in an array needs a `key` prop: a string or number that uniquely identifies that item among its siblings. Keys tell React which item is which between renders. When you insert, delete or reorder items, React uses keys to move the existing DOM nodes and component state along with the right data instead of rebuilding everything or, worse, pairing state with the wrong item.

Good keys come from the data: database ids, slugs, or ids you generate once when the item is created (crypto.randomUUID()). Rules:

  • Keys must be unique among siblings, not globally.
  • Keys must be stable: the same item gets the same key on every render.
  • Don't use the array index if the list can be reordered, filtered or have items inserted; the index then refers to a different item and any state (like a typed-in input) follows the position, not the item. Index is acceptable only for static lists.
  • Never generate keys during render (key={Math.random()}); every render then creates brand-new components.

The key goes on the outermost element returned from map. If you need to return several elements per item, use <Fragment key={id}> (import Fragment from react), since the short <> syntax can't take a key.

Keys are not passed to your component as a prop; pass the id separately if the child needs it.

Example

import { Fragment } from "react";

const people = [
  { id: "a1", name: "Creola Johnson", profession: "chemist" },
  { id: "b2", name: "Mario Molina", profession: "chemist" },
  { id: "c3", name: "Percy Julian", profession: "chemist" },
  { id: "d4", name: "Subrahmanyan Chandrasekhar", profession: "astrophysicist" },
];

export default function Scientists() {
  const chemists = people.filter(p => p.profession === "chemist");

  return (
    <dl>
      {chemists.map(p => (
        // several elements per item: keyed Fragment
        <Fragment key={p.id}>
          <dt>{p.name}</dt>
          <dd>{p.profession}</dd>
        </Fragment>
      ))}
    </dl>
  );
}

Remember

  • Render arrays with map (and filter); each element needs a key
  • Keys must be unique among siblings and stable across renders, ideally ids from the data
  • Avoid index keys for lists that change order or contents; never use random keys

Watch out for

  • Using the array index as key in an editable or sortable list, so input state ends up on the wrong row.

Check yourself

  1. A todo list uses key={index}. You delete the first todo; each row has an uncontrolled text input. What happens?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: react.dev/learn/rendering-lists