cf.completefrontend
Open lab My progress
THE REACT FIELD GUIDE

Rendering lists

Turn arrays into lists of React components with map and filter, and see why each item needs a key: run the experiment where index keys mix up typed text.

By the end, you can
  • 01
    Render a list from dataTurn an array into elements or components with map, after picking and ordering items with filter and toSorted.
  • 02
    Explain what a key doesSay how React uses keys to match the elements of a new list to the old ones, and why index keys can put typed text next to the wrong item.
  • 03
    Choose a good keyPick a key that is unique among its siblings and the same on every render, put it in the right place, and read React's key warnings.

From an array of data to a list on the page

Most of what a shop page shows comes from lists: products, cart items, orders, reviews. The data usually arrives as an array of objects, and React has no special loop for showing it. You turn the array of data into an array of JSX elements with JavaScript's map, and React shows that array. The result is live under the code:

Three products, one list item eachPop out in the code editor (opens in a new tab)JSX
const products = [  { id: "tea", name: "Tea", price: 3 },  { id: "cake", name: "Cake", price: 4 },  { id: "lemonade", name: "Lemonade", price: 2 },]; export default function App() {  const items = products.map((product) => (    <li key={product.id}>      {product.name}: ${product.price}    </li>  ));  return <ul>{items}</ul>;}
ResultLoading…

Lines 1 to 5 are the data: three product objects, each with an id, a name, and a price. Line 8 calls products.map, which calls the arrow function once for each product and collects what it gives back into a new array. The function gives back one <li> (lines 9 to 11), so items holds three elements. Line 13 puts the whole array between curly braces, and React shows each element in order: Tea: $3, Cake: $4, and Lemonade: $2.

The key on line 9 is the one new thing. It gives each <li> a name that stays with its product: "tea", "cake", "lemonade". React uses it to tell the items apart when the list changes. Most of this lesson is about why that matters.

Press Edit & run and add a product after line 4: { id: "coffee", name: "Coffee", price: 5 },. A fourth item appears. You changed the data, and the list followed.

A crate named products holds three cards: Tea $3, Cake $4, Lemonade $2. An arrow labelled map leads to a crate named items holding three element cards: li key="tea", li key="cake", li key="lemonade". An arrow labelled React leads to a page window listing Tea: $3, Cake: $4, Lemonade: $2.Tea, $3Cake, $4Lemonade, $2productsyour datamap<li key="tea"><li key="cake"><li key="lemonade">itemselements with keysReactpageTea: $3Cake: $4Lemonade: $2the list
map calls the arrow function once per product and gives back a new array of three <li> elements, each with the product's id as its key. React shows the array in order: three list items.
Real-life analogyA coat check

At a coat check, the attendant ties a numbered ticket to your coat. Coats come and go and slide along the rail all evening, and your ticket still finds your coat. If the attendant only remembered "the third hook from the left", you would leave with whatever coat hangs there now.

In real life: The coats handed in at the desk
In React: The items in your array
In real life: A numbered ticket tied to each coat
In React: key={item.id}: a name that stays with the item
In real life: "The third hook from the left"
In React: key={index}: a position, not an item
In real life: Coats slide along the rail when one is collected
In React: Items move when one is removed, added, or sorted
In real life: The ticket still finds your coat
In React: The key still finds the item's element
The main idea

To render a list, turn an array of data into an array of JSX elements with map, after filter or a sort if you need one. Give each element a key: a string or a number that is unique among its siblings and the same on every render. React matches the elements of the new list to the old ones by key, so each item keeps its own element, and anything typed into it, when the list changes.

map: one component for each item

Real lists show more than a line of text per item, so the map usually gives back a component. Here each product becomes a ProductCard:

A shelf of product cards from an arrayPop out in the code editor (opens in a new tab)JSX
const products = [  { id: "tea", name: "Tea", price: 3 },  { id: "cake", name: "Cake", price: 4 },  { id: "lemonade", name: "Lemonade", price: 2 },]; function ProductCard({ name, price }) {  return (    <li className="card">      <h3>{name}</h3>      <p>${price}</p>    </li>  );} export default function App() {  return (    <ul className="shelf">      {products.map((product) => (        <ProductCard key={product.id} name={product.name} price={product.price} />      ))}    </ul>  );}
ResultLoading…

Lines 7 to 14 define ProductCard. It takes name and price as props and returns one card as an <li>. Line 19 maps over the products, and line 20 gives back one <ProductCard> for each, with the product's name and price as props. The styles.css tab lays the cards out side by side. The result shows three cards: Tea, Cake, and Lemonade.

Look at where the key is: on line 20, on <ProductCard> inside the map, not on the <li> inside ProductCard. The key belongs on the element the map gives back, because those elements are the items of the list. Inside ProductCard, the <li> has no siblings to be told apart from.

Line 19's arrow function has round brackets after the =>: it gives back the JSX inside them. With curly braces instead, the body is a block of statements, and it gives back nothing unless it says return. A map that gives back nothing makes an array of undefined, and the list comes out empty, with no error. Exercise 2 shows that bug.

Sometimes each item needs more than one element, like a term and its description in a <dl>. Wrap them in a Fragment. The short <>…</> can't take a key, so write <Fragment>:

Two elements per drink, in a keyed FragmentPop out in the code editor (opens in a new tab)JSX
import { Fragment } from "react"; const drinks = [  { id: "tea", name: "Tea", about: "Hot, with lemon or milk." },  { id: "lemonade", name: "Lemonade", about: "Cold, with fresh mint." },]; export default function App() {  return (    <dl>      {drinks.map((drink) => (        <Fragment key={drink.id}>          <dt>{drink.name}</dt>          <dd>{drink.about}</dd>        </Fragment>      ))}    </dl>  );}
ResultLoading…

Line 1 imports Fragment from react. Line 12 wraps each drink's <dt> and <dd> (lines 13 and 14) in a <Fragment key={drink.id}>. The Fragment adds nothing to the page: the <dl> holds the four elements directly, and the result shows each drink with its description under it.

filter and sort before you map

To show only some items, pick them first with filter, then map what's left. Here one array of products makes two parts of the page:

Available and sold-out products from one arrayPop out in the code editor (opens in a new tab)JSX
const products = [  { id: "tea", name: "Tea", stock: 5 },  { id: "cake", name: "Cake", stock: 0 },  { id: "lemonade", name: "Lemonade", stock: 2 },  { id: "coffee", name: "Coffee", stock: 0 },]; export default function App() {  const available = products.filter((product) => product.stock > 0);  const soldOut = products.filter((product) => product.stock === 0);   return (    <>      <h3>Available ({available.length})</h3>      <ul>        {available.map((product) => (          <li key={product.id}>{product.name}</li>        ))}      </ul>      {soldOut.length > 0 && (        <p>Sold out: {soldOut.map((product) => product.name).join(", ")}</p>      )}    </>  );}
ResultLoading…

Line 9 keeps the products with stock > 0, and line 10 the ones with none. Neither changes products: filter gives back a new array. Line 14 shows how many are available, and lines 16 to 18 map them to list items: Tea and Lemonade. Line 20 is the conditional rendering from the last lesson: the sold-out line shows only when there is something sold out.

Line 21 maps too, but to strings, not elements, and join(", ") turns them into one string: Sold out: Cake, Coffee. One string isn't a list of elements, so it needs no keys. Press Edit & run and change Cake's stock on line 3 to 1: Cake moves into the list, and the heading says Available (3).

To change the order, sort a copy. sort() sorts the array you call it on, in place, and your data should stay as it came (the Keeping components pure lesson explains why). Use toSorted, which gives back a sorted new array, or copy first with [...products].sort(…):

Cheapest first, without changing the dataPop out in the code editor (opens in a new tab)JSX
const products = [  { id: "tea", name: "Tea", price: 3 },  { id: "cake", name: "Cake", price: 4 },  { id: "lemonade", name: "Lemonade", price: 2 },]; export default function App() {  const sorted = products.toSorted((a, b) => a.price - b.price);  return (    <ol>      {sorted.map((product) => (        <li key={product.id}>          {product.name}: ${product.price}        </li>      ))}    </ol>  );}
ResultLoading…

Line 8 makes sorted: the products by price, lowest first. a.price - b.price is below zero when a is cheaper, which puts a first. Line 11 maps the sorted copy, and the result reads Lemonade: $2, Tea: $3, Cake: $4. products itself still starts with Tea.

Why keys matter: which element is which

When the data changes, React calls your component again and gets a new list of elements. It doesn't throw the old page away: it works out which new element is which old one, and keeps those DOM elements, changing only what differs. For a list, the key is how it matches them. An element with the same key as before, and the same type, like <li>, is "the same item": React keeps its DOM element and anything inside it. A key that's new gets a new element, and an old key that's gone has its element removed. This matching is called reconciliation; Reconciliation and the diffing algorithm, in the course's last stage, follows it through React's own code.

You can't see that matching while the list only shows text. You can see it once each row holds something of its own, like a box you typed into. This cart uses each item's position, index, as its key, with a bug on purpose:

Gift notes keyed by position (a bug on purpose)Pop out in the code editor (opens in a new tab)JSX
import { useState } from "react"; const cart = [  { id: "tea", name: "Tea" },  { id: "cake", name: "Cake" },  { id: "lemonade", name: "Lemonade" },]; export default function App() {  const [items, setItems] = useState(cart);   function remove(id) {    setItems(items.filter((item) => item.id !== id));  }   return (    <ul>      {items.map((item, index) => (        <li key={index}>          {item.name}          <input placeholder="Gift note" />          <button onClick={() => remove(item.id)}>Remove</button>        </li>      ))}    </ul>  );}
ResultLoading…

Line 10 keeps the cart in state, a value React remembers for the component (state gets its own lesson, State: a component's memory). remove on lines 12 to 14 makes a new array without one item. Line 18 maps the items with their position, index, and line 19 uses it as the key. Each row has the name (line 20), a box for a gift note (line 21), and a Remove button (line 22). The styles.css tab lines the rows up.

Type for Asha in Tea's box, then click Remove on Tea's row. Tea is gone, but for Asha now sits next to Cake. Here is what React saw. Before, the keys were 0, 1, and 2, for Tea, Cake, and Lemonade. After, the keys are 0 and 1, for Cake and Lemonade. Key 0 is still there, so React kept that <li>, with its box and your text, and changed its name from Tea to Cake. Key 1 changed from Cake to Lemonade. Key 2 is gone, so React removed the last row. The text in a box belongs to the box's element, and React never had a reason to touch it.

Now press Edit & run, change line 19 to <li key={item.id}>, and try again. This time the keys are tea, cake, and lemonade. After the removal, tea is gone, so React removes Tea's row, with your note. Cake and Lemonade keep their own rows.

Two halves. Top, key={index}: before, elements with keys 0, 1 and 2 show Tea with the note for Asha, Cake, and Lemonade. After Remove on Tea, keys 0 and 1 remain: arrows join key 0 to key 0, which now shows Cake with the note for Asha, and key 1 to key 1, now Lemonade. Key 2 is crossed out. Bottom, key={item.id}: before, tea with the note, cake, and lemonade. After, the tea element is crossed out, and arrows join cake to cake and lemonade to lemonade.key={index}beforeafter Remove on Tea0: Tea, for Asha1: Cake2: Lemonade0: Cake, for Asha1: LemonadeTea's note is next to Cakekey={item.id}beforeafter Remove on Teatea: Tea, for Ashacake: Cakelemonade: Lemonadecake: Cakelemonade: Lemonadethe note left with Tea
React matches the elements of the new list to the old ones by key. With key={index}, key 0 still exists after Tea is removed, so React keeps that element, with the note in its box, and only changes its text to Cake. With key={item.id}, the tea element goes, and the note goes with it.

The playground below makes the matching visible. It numbers every <li> the first time React puts it on the page, so a number that stays is the same element. Pick a key, press Fill in notes, and then change the list.

Playground: which element is which?
NoteList with key={index}JSX
function NoteList({ items }) {  return (    <ul>      {items.map((item, index) => (        <li key={index}>          {item.name}          <input placeholder="Gift note" />        </li>      ))}    </ul>  );}
On the page3 items
  • Tea
  • Cake
  • Lemonade
Try it yourself
key={index}

Type a note next to Tea, or press Fill in notes. Then remove, add, or reverse, and watch the numbers and the notes.

React renders this list on the page with the key you pick, and starts it over when you pick another. The numbers aren't part of the code: the page numbers each <li> the first time React puts it on the page, so a number that stays is the same element.

With key={index}, press Remove first: #1 and #2 stay and change their names, #3 goes, and "Tea's note" ends up next to Cake. Press Reset, pick key={item.id}, fill in the notes, and press Reverse: the numbers travel with their items, notes and all, because React moves the elements instead of relabeling them. Add to top shows the same: with the index, every row gets a new name and a new #4 appears at the end; with the id, only the new item gets a new element.

The same goes for anything a row keeps for itself: the text in a box, whether a checkbox is ticked, where the keyboard focus is, and a component's own state, as Preserving and resetting state shows later. The key decides which item they belong to.

What React says when a key is missing

Leave the key out, and React still shows the list, matching the items by position, the same as the index. It also warns you:

A list without keysPop out in the code editor (opens in a new tab)JSX
const products = ["Tea", "Cake", "Lemonade"]; export default function App() {  return (    <ul>      {products.map((name) => (        <li>{name}</li>      ))}    </ul>  );}
ResultLoading…

Line 7 has no key. The list shows, and the console under the result shows React's warning:

React's warning for a list without keysText
Each child in a list should have a unique "key" prop.

Check the render method of `App`. See https://react.dev/link/warning-keys for more information.

"Each child in a list" means each element in the array. The second line names the component whose list it is, App, so you know where to look. Add key={name} on line 7, since every name in this list is different, and the warning goes. React prints its warnings while you develop; a production build leaves them out.

Choosing a good key

A good key has two properties:

  • Unique among its siblings. No two items in the same list share it. Two different lists can use the same keys.
  • The same on every render. It stays with its item, whatever position the item moves to.

The best keys come with the data. Products, orders, and users from a server usually have an id from the database. When there isn't one, look for a field or a set of fields that together are unique. A name alone often isn't:

Two products with the same namePop out in the code editor (opens in a new tab)JSX
const products = [  { shop: "Corner Café", name: "Tea" },  { shop: "Corner Café", name: "Cake" },  { shop: "Station Kiosk", name: "Tea" },]; export default function App() {  return (    <ul>      {products.map((product) => (        <li key={product.name}>          {product.name} from {product.shop}        </li>      ))}    </ul>  );}
ResultLoading…

Lines 2 and 4 are both Tea, from different shops, so line 11 gives two items the key Tea. The list still shows all three, but React warns in the console:

React's warning for a repeated keyText
Encountered two children with the same key, `Tea`. Keys should be unique so that components maintain their identity across updates.

Its full text goes on to say that a repeated key can make items appear twice or go missing when the list changes. Press Edit & run and change line 11 to <li key={product.shop + product.name}>: the shop and the name together are unique, and the warning goes.

For items made in the browser, like a line the reader adds to a list, make the id when you create the item, in the code that adds it: a counter, or crypto.randomUUID(). Never make it in the map. key={Math.random()} or key={crypto.randomUUID()} written there runs again on every render, so every key is new every time:

Left, the first render: three crossed-out elements with keys 0.42 Tea, 0.91 Cake, 0.17 Lemonade, labelled removed. Right, the next render: three new elements with keys 0.63 Tea, 0.08 Cake, 0.55 Lemonade, labelled made new. In the middle: no key matches.first rendernext render0.42: Tea0.91: Cake0.17: Lemonade0.63: Tea0.08: Cake0.55: Lemonadeno key matchesso nothing is keptremoved, with what was typedmade new, empty
Math.random() runs again on every render, so every key is new. React finds no match for any of them: it removes all three <li> elements and makes three new ones, and anything typed into them is lost.

Try it in the playground above: pick key={Math.random()}, fill in the notes, and press any button. Every number changes, and every note is gone: React found no matching key, removed every row, and made new ones.

The index is fine in one case: a list that never changes order, never has items added or removed anywhere but the end, and holds nothing of its own, like a fixed list of footer links. The cart in Why React? only adds and removes at the end, which is why its key={index} worked.

One more rule: key is for React, not for your component. React takes it out of the props, so a component never receives it. If a component needs the id, pass it again under another name:

Passing the id as a prop of its ownPop out in the code editor (opens in a new tab)JSX
const products = [  { id: "tea", name: "Tea" },  { id: "cake", name: "Cake" },]; function CartRow({ id, name }) {  return (    <li>      {name} <small>item {id}</small>    </li>  );} export default function App() {  return (    <ul>      {products.map((product) => (        <CartRow key={product.id} id={product.id} name={product.name} />      ))}    </ul>  );}
ResultLoading…

Line 18 gives each CartRow the same value twice: as key, for React, and as id, for the component. Line 6 reads id, and line 9 shows it: Tea item tea. Press Edit & run and change id to key on lines 6 and 9. The ids disappear, and the console says CartRow: `key` is not a prop. Trying to access it will result in `undefined` being returned.

How to read this table: each row is a kind of key, with the two questions a good key must pass, and the verdict.

Which key to use
KeyUnique among its siblings?The same on every render?Use it?
item.id from your dataYes, when every item has its own id.Yes. It comes with the item.Yes. This is the usual choice.
Several fields together, like shop + nameYes, when no two items share all of them.Yes, while those fields don't change.Yes, when the data has no id.
index, the positionYes.No. After a sort, a removal, or an item added at the top, it belongs to another item.Only in a list that never changes order.
Math.random() or crypto.randomUUID() inside the mapAlmost always.No. It's new on every render.No. Make the id once, when you create the item.
No key at allReact uses the index.No, as with the index.No. React warns, and the index's problems come with it.

A shopping list that keeps its numbers

Here is everything together: a shopping list where you add items, set how many of each you need, sort, and remove. Add Bread, set Tea to 3, then press Sort A to Z.

A shopping list with ids made when items are addedPop out in the code editor (opens in a new tab)JSX
import { useState } from "react"; let nextId = 3; export default function App() {  const [items, setItems] = useState([    { id: 1, name: "Tea" },    { id: 2, name: "Cake" },  ]);  const [name, setName] = useState("");   function add() {    if (name.trim() === "") return;    setItems([...items, { id: nextId++, name: name.trim() }]);    setName("");  }   function remove(id) {    setItems(items.filter((item) => item.id !== id));  }   function sortByName() {    setItems(items.toSorted((a, b) => a.name.localeCompare(b.name)));  }   return (    <>      <input        value={name}        onChange={(event) => setName(event.target.value)}        placeholder="Add an item"      />      <button onClick={add}>Add</button>      <button onClick={sortByName}>Sort A to Z</button>      <ul>        {items.map((item) => (          <li key={item.id}>            {item.name}            <input type="number" defaultValue={1} min={1} aria-label={`How many ${item.name}`} />            <button onClick={() => remove(item.id)}>Remove</button>          </li>        ))}      </ul>      {items.length === 0 && <p>Your list is empty.</p>}    </>  );}
ResultLoading…

The list is sorted, Bread, Cake, Tea, and Tea still needs 3. Each number box is an <input> that keeps what you typed (line 39), and each row's key is the item's id (line 37), so React moved the rows instead of relabeling them.

  • Line 3 starts a counter, and line 14 gives every new item the next number as its id, once, when add creates it.
  • Line 19 removes an item by its id with filter, and line 23 sorts a copy with toSorted and localeCompare, which compares text the way a dictionary does.
  • Line 44 shows a message once the list is empty: conditional rendering again.

Press Edit & run and change line 37 to <li key={index}> and line 36 to {items.map((item, index) => (. Then do the same steps: after the sort, the 3 sits next to Bread, in the first row, where Tea used to be.

Good key or a source of bugs?
  • key={product.id}, with ids from the shop's database
  • key={index}, in a cart you can sort and remove from
  • key={Math.random()}
  • key={order.number}, where every order has its own number
  • key={product.name}, where two shops both sell Tea
  • key={crypto.randomUUID()}, written inside the map
  • key={index}, in a footer's fixed list of links that never changes
  • key={item.id}, where the id came from a counter when the item was added
Try it yourself
0 of 8 correct

Each card is a key and where it's used. Sort it: is it a good key there, or can it cause bugs?

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common mix-ups

  • "Keys are only there to make lists faster." No. They decide which element belongs to which item. A wrong key puts typed text, ticks, and state next to the wrong item.
  • "key={index} fixes the warning, so it's fine." It only silences the warning. Without a key, React already uses the index, so the bug stays the same.
  • "Keys must be unique on the whole page." No. Only among siblings, the items of one list. Two lists can both use tea.
  • "Math.random() makes a good unique key." It's unique, but it's new on every render, so React makes every element again.
  • "A component can read its own key." No. props.key is undefined. Pass the value as another prop too, like id.
  • "The key goes on the element inside the component." No. It goes on the element the map gives back, like <ProductCard key={product.id} />.

Practice exercises

Each exercise shows its result under the code, or when you press Edit & run. Change the code, and the result updates as you type. For the first one, answer before you run anything.

Exercise 1 · Warm-upPredict the list

How many list items does this show? Answer first, then press Edit & run to check.

const products = [
  { id: "tea", name: "Tea", price: 3 },
  { id: "cake", name: "Cake", price: 4 },
  { id: "lemonade", name: "Lemonade", price: 2 },
  { id: "coffee", name: "Coffee", price: 5 },
];

export default function App() {
  return (
    <ul>
      {products
        .filter((product) => product.price < 4)
        .map((product) => (
          <li key={product.id}>{product.name}</li>
        ))}
    </ul>
  );
}

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upFind the missing list

    The list is empty, and there's no error. How many list items does the starter show? Then fix it so all three show.

    const products = [  { id: "tea", name: "Tea" },  { id: "cake", name: "Cake" },  { id: "lemonade", name: "Lemonade" },]; export default function App() {  return (    <ul>      {products.map((product) => {        <li key={product.id}>{product.name}</li>;      })}    </ul>  );}
    ResultLoading…

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeAdd the keys

      React warns that this list has no keys. Add a good key. What did you write after key=?

      const products = [  { id: "tea", name: "Tea" },  { id: "cake", name: "Cake" },  { id: "lemonade", name: "Lemonade" },]; export default function App() {  return (    <ul>      {products.map((product) => (        <li>{product.name}</li>      ))}    </ul>  );}
      ResultLoading…

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeOnly what's in stock

        The list shows a sold-out product too. Show only products with stock left. Which products does the list show?

        const products = [  { id: "tea", name: "Tea", stock: 5 },  { id: "cake", name: "Cake", stock: 0 },  { id: "lemonade", name: "Lemonade", stock: 2 },]; export default function App() {  return (    <ul>      {products.map((product) => (        <li key={product.id}>          {product.name} ({product.stock} left)        </li>      ))}    </ul>  );}
        ResultLoading…

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeKeep notes with their items

          Fix the key so a note stays with its item. Then type for Asha next to Tea and press Reverse. Which item has the note?

          import { useState } from "react"; const cart = [  { id: "tea", name: "Tea" },  { id: "cake", name: "Cake" },  { id: "lemonade", name: "Lemonade" },]; export default function App() {  const [items, setItems] = useState(cart);   return (    <>      <button onClick={() => setItems(items.toReversed())}>Reverse</button>      <ul>        {items.map((item, index) => (          <li key={index}>            {item.name}            <input placeholder="Gift note" />          </li>        ))}      </ul>    </>  );}
          ResultLoading…

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeOrders and their items

            Show each order as a heading, like Order 7, with a list of its items. Give every list the keys it needs. How many list items does the page show?

            const orders = [  { id: 7, items: ["Tea", "Cake"] },  { id: 8, items: ["Tea", "Lemonade", "Cake"] },]; export default function App() {  return (    <>      {/* For each order: a heading, Order 7, and a list of its items. */}    </>  );}
            ResultLoading…

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each key question, ask what the key was before a change and what it is after. Press Edit & run on a question's code to check your answer.

              The rendering lists quiz · 7 questionsScore: first tries count
              1. Question 1 of 7What does React use to tell list items apart when you don't give them a key?

                Choose an answer to see the explanation.

              2. Question 2 of 7In this cart, you type for Asha in Tea's box, then click Remove on Tea's row. Which item's box shows for Asha?

                Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                import { useState } from "react";
                
                const cart = [
                  { id: "tea", name: "Tea" },
                  { id: "cake", name: "Cake" },
                  { id: "lemonade", name: "Lemonade" },
                ];
                
                export default function App() {
                  const [items, setItems] = useState(cart);
                
                  function remove(id) {
                    setItems(items.filter((item) => item.id !== id));
                  }
                
                  return (
                    <ul>
                      {items.map((item, index) => (
                        <li key={index}>
                          {item.name}
                          <input placeholder="Gift note" />
                          <button onClick={() => remove(item.id)}>Remove</button>
                        </li>
                      ))}
                    </ul>
                  );
                }

                Choose an answer to see the explanation.

              3. Question 3 of 7Which key is best for products loaded from a shop's database?

                Choose an answer to see the explanation.

              4. Question 4 of 7What goes wrong with key={Math.random()}?

                Choose an answer to see the explanation.

              5. Question 5 of 7Both lists use Tea as a key. Does React warn about it?

                Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                const drinks = ["Tea", "Lemonade"];
                const favorites = ["Cake", "Tea"];
                
                export default function App() {
                  return (
                    <>
                      <ul>
                        {drinks.map((name) => (
                          <li key={name}>{name}</li>
                        ))}
                      </ul>
                      <ul>
                        {favorites.map((name) => (
                          <li key={name}>{name}</li>
                        ))}
                      </ul>
                    </>
                  );
                }

                Choose an answer to see the explanation.

              6. Question 6 of 7What does Row show?

                Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                function Row(props) {
                  return <li>Item: {props.key}</li>;
                }
                
                export default function App() {
                  return (
                    <ul>
                      <Row key="tea" />
                    </ul>
                  );
                }

                Choose an answer to see the explanation.

              7. Question 7 of 7React warns that the list in this code needs keys. Where does the key go?

                Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                const products = [
                  { id: "tea", name: "Tea" },
                  { id: "cake", name: "Cake" },
                ];
                
                function ProductCard({ name }) {
                  return <li>{name}</li>;
                }
                
                export default function App() {
                  return (
                    <ul>
                      {products.map((product) => (
                        <ProductCard name={product.name} />
                      ))}
                    </ul>
                  );
                }

                Choose an answer to see the explanation.

              Key takeaways

              • Turn an array of data into an array of elements with map, and put it in curly braces. Use filter and toSorted first to pick and order the items, without changing the data.
              • Give each element in the array a key, on the element the map gives back. Use <Fragment key> for several elements per item.
              • React matches the new list to the old one by key: same key, same element, with what was typed into it. Without keys it matches by position, and warns.
              • A good key is unique among its siblings and the same on every render: an id from the data, or one made when the item is created. Not the index in a list that changes order, and never Math.random().
              • key isn't a prop. If the component needs the id, pass it again as another prop.

              Remember the one-liner.
              map the data to elements, and give each one a key that stays with its item.

              Coming next: Keeping components pure. It shows why a component should only calculate while it renders, and how StrictMode helps you catch the ones that don't.

              CompleteFrontend Clear concepts. Working examples.