Skip to Content
React in a Week
Day 2 · Describing the UI

Rendering lists

Turn arrays of data into lists of components with map and filter, and give each item a stable key.

In the props lesson you wrote <BookCard book={...} /> once for every book. That works for three books, but a real reading list could hold fifty, and the books change as you add and remove them. You can't hand-write a line of JSX for data you don't know yet.

The fix is to keep your books in an array and let JavaScript produce one component per item. You already met the tool for this on Day 1: the array method map.

From an array to elements with map

map calls a function for every item in an array and collects the results into a new array. If that function returns JSX, you get an array of JSX, and React knows how to render an array of JSX:

Live code· map turns data into JSX

You'll usually skip the items variable and write the map call straight inside the JSX braces. Here is the same idea with book objects and a BookCard component:

Live code· One BookCard per book

Open data.js and add a fourth book. The page updates without touching App.js at all. That is the payoff: the JSX describes how one book looks, and the data decides how many there are.

Showing only some items with filter

Often you want part of a list, such as only the books you're reading right now. Use filter to make a smaller array first, then map over the result:

Live code· filter, then map

filter and map both return new arrays. They never change books itself. That matters, as you'll see in the next lesson on pure components.

Keeping list items in order with key

Every example so far has put a key on the outermost element inside map. Here is what happens without one. Open the console:

Live code· A list without keys

The page still looks right, but React logs a warning saying each child in a list should have a unique key prop. Add key={book.id} to the <li> and the warning goes away.

A key is a name tag for a list item. Later, when the list changes (you add a book, delete one or sort them), React compares the old list with the new one. Keys let it match "this item now" with "that item before", so it updates the right thing instead of guessing by position.

The rules for keys

  • Keys come from your data. Books from a database already have IDs. For books you create in the browser, you'll generate an ID once with crypto.randomUUID() when the book is added (on Day 3).
  • Keys must be unique among siblings. Two items in the same list can't share a key. Items in different lists can reuse the same keys.
  • Keys must not change between renders. Never generate them during rendering, as in key={Math.random()}. A new key every time makes React throw away and rebuild every item on each render.
  • Put the key on the outermost element returned from map. In books.map((book) => <BookCard key={book.id} book={book} />) the key goes on BookCard, not on something inside BookCard.

Your components do not receive key as a prop. It is a hint meant only for React. If BookCard needs the ID itself, pass it separately: <BookCard key={book.id} id={book.id} book={book} />. In this course BookCard already receives the whole book, so it can read book.id directly.

Deep diveKeys on a group of elements

Sometimes each item renders more than one element without a wrapper, using a Fragment. The short <>...</> syntax can't take a key, so import Fragment and write it out in full:

import { Fragment } from 'react';

function Glossary({ terms }) {
  return (
    <dl>
      {terms.map((term) => (
        <Fragment key={term.id}>
          <dt>{term.word}</dt>
          <dd>{term.meaning}</dd>
        </Fragment>
      ))}
    </dl>
  );
}

A Fragment adds nothing to the page. It only groups the dt and dd so the key has somewhere to live.

Quick check

Which is the best key for a book in the Reading List?

Extracting a BookList component

The map call is a good candidate for its own component. BookList takes an array of books as a prop and renders one BookCard each. It can also handle the empty case, which combines today's lessons nicely:

Live code· BookList component

Now App doesn't need to know how a list is drawn. It hands over the data, and BookList does the rest.

Try it

Shelves by status

Use filter and map to render three sections: "Want to read", "Reading" and "Done". Each section lists the titles of the books with that status, and each li needs a proper key. As a bonus, make a small Shelf component so you don't repeat the same JSX three times.

Live code· Challenge: shelves
Show solution

Shelf takes a heading and an already-filtered array. App filters once per status and passes the result down.

Live code· Solution: shelves

The keys go on the li because that is the outermost element returned from map. The three Shelf components don't need keys, because they are written out by hand, not produced by map.

Quick check

BookCard returns an article element, and BookList renders books.map(book => <BookCard book={book} />). Where should the key go?

Day 2 progress

0/7
Next: Keeping components pure

On this page