Rendering Lists in React: map(), Keys, Filter and Sort Explained

React.js Full Course: Beginner to Advanced · Start Here · 8 min read · Updated

From data to a list

  1. Start with an array of objects. Each one has a unique id.
  2. map turns each object into an element, such as an li or a card.
  3. Give each element a key from your data. Never use a random key.
  4. React uses the keys to know which item is which, so it updates the right ones and keeps their state.
  5. To change the list, make a new array with spread, filter or map. Never change the old one.

Almost every real app shows lists: products, messages, tasks, comments, search results. In React, you turn an array of data into a list of elements. You do it with a JavaScript method you may already know: map. This lesson explains how, and also the one thing beginners often miss: the key.

The map method in one minute

map goes through an array and makes a new array by running a function on each item. It does not change the original array.

const numbers = [1, 2, 3];
const doubled = numbers.map((n) => n * 2);
// doubled is [2, 4, 6]

In React we use map to turn data into JSX. React knows how to show an array of elements, one after the other.

Showing a list

const fruits = ['Apple', 'Banana', 'Mango'];

function FruitList() {
  return (
    <ul>
      {fruits.map((fruit) => (
        <li key={fruit}>{fruit}</li>
      ))}
    </ul>
  );
}

The curly braces open JavaScript inside the ul. fruits.map(...) gives an array of li elements, and React shows them. Notice the round brackets after the arrow: they let us return JSX over several lines.

A list of objects

Most real lists are arrays of objects. Each object has an id that makes it unique:

const products = [
  { id: 1, name: 'Notebook', price: 3 },
  { id: 2, name: 'Pen', price: 1 },
  { id: 3, name: 'Bag', price: 20 },
];

function ProductList() {
  return (
    <div>
      {products.map((product) => (
        <ProductCard key={product.id} name={product.name} price={product.price} />
      ))}
    </div>
  );
}

Here every item becomes a ProductCard component. This is the most common shape: a list component that maps data into item components. Note that the key goes on the element returned directly inside the map, here the ProductCard.

What is a key and why does React want it?

If you forget the key, React shows a warning in the console: “Each child in a list should have a unique key”. A key is a special prop that gives each item a name. React uses it to remember which item is which, between draws.

Imagine a list of three tasks, and you delete the first one. Without keys, React only knows there are now two items in a row, and may reuse the wrong boxes. With keys, React knows that task 1 is gone, and tasks 2 and 3 are the same as before. This matters most when items have state, such as an input with typed text, or a checkbox. Without good keys, the typed text can jump to the wrong item.

A key is not passed as a prop. Inside the component you cannot read props.key. If you need the id, pass it again with another name, such as id={product.id}.

Rules for keys

  • Keys must be unique among the siblings in the same list. They do not need to be unique in the whole app.
  • Keys must be stable: the same item should keep the same key every time. Use an id from your data, such as a database id.
  • Do not make keys with `Math.random()`. A new key every draw makes React throw away and re-make every item, which is slow and loses state.
  • Avoid the array index as a key when the list can be re-ordered, filtered or changed. Index as a key is fine only for a list that never changes.
// Good
{tasks.map((task) => <li key={task.id}>{task.text}</li>)}

// OK only for a fixed list that never changes
{days.map((day, index) => <li key={index}>{day}</li>)}

// Bad: a new key every draw
{tasks.map((task) => <li key={Math.random()}>{task.text}</li>)}

If your data has no ids, give each item an id when you create it, for example with crypto.randomUUID(), and keep it in the data.

Keys with fragments

If each item needs to return two elements side by side, you use a fragment. The short form <> cannot take a key, so use the long form:

import { Fragment } from 'react';

{items.map((item) => (
  <Fragment key={item.id}>
    <dt>{item.term}</dt>
    <dd>{item.meaning}</dd>
  </Fragment>
))}

Filtering a list

filter makes a new array with only the items that pass a test. Combine it with map to show only some items. Do not remove items from your data just to hide them. Keep the full list in state, and filter when you draw.

function ProductList({ products }) {
  const [onlyCheap, setOnlyCheap] = useState(false);

  const visible = onlyCheap ? products.filter((p) => p.price < 5) : products;

  return (
    <div>
      <label>
        <input type="checkbox" checked={onlyCheap} onChange={(e) => setOnlyCheap(e.target.checked)} />
        Only cheap items
      </label>
      <ul>
        {visible.map((p) => <li key={p.id}>{p.name} - {p.price}</li>)}
      </ul>
    </div>
  );
}

The list visible is not state. It is calculated from state and props during the draw. This is a good habit: do not store in state what you can calculate.

Sorting a list

Careful: the sort method changes the array in place. If you call it on state or props, you break the rule that data must not be changed. Make a copy first with the spread syntax, or use toSorted in modern browsers:

const sorted = [...products].sort((a, b) => a.price - b.price);
// or: const sorted = products.toSorted((a, b) => a.price - b.price);

For text, sort with a.name.localeCompare(b.name), which also handles letters from other languages correctly.

Showing a message for an empty list

When the list is empty, show something helpful, as the conditional rendering lesson explained:

{visible.length === 0 ? (
  <p>No products match your search.</p>
) : (
  <ul>{visible.map((p) => <li key={p.id}>{p.name}</li>)}</ul>
)}

Adding and removing items

Because state must not be changed directly, adding and removing use a new array:

// add
setTasks([...tasks, { id: crypto.randomUUID(), text: 'Buy milk', done: false }]);

// remove
setTasks(tasks.filter((t) => t.id !== idToRemove));

// toggle one
setTasks(tasks.map((t) => (t.id === id ? { ...t, done: !t.done } : t)));

You will use these three lines in almost every project you build. It is worth typing them until they feel natural.

Common mistakes

  • Forgetting the key and ignoring the warning.
  • Putting the key on the wrong element: it belongs on the outermost element returned by map.
  • Using the index as a key in a list that changes, which causes input values to jump around.
  • Calling sort or reverse on state without copying.
  • Writing forEach instead of map. forEach returns nothing, so nothing is shown.

Why the key matters: a small experiment

You can see the effect of keys yourself. Make a list of three items where each has an input box. Use the index as the key, and add a button that adds a new item at the start of the list. Type something in the first box, and then press the button. You will notice that the text you typed stays in the first position, even though it now belongs to a different item. React matched the boxes by position, so the typed text stayed in the first box.

Now change the key to the item's id and try again. The typed text moves along with its item, which is what you expect. This is the whole story of keys: they tell React which item is which, so the right state stays with the right item. It is a small change in the code and prevents very confusing bugs.

Your task

  1. Make a list of five books with id, title and year. Show them with map and correct keys.
  2. Add a text box that filters the books by title.
  3. Add a button that sorts by year, without changing the original array.
  4. Add a message for when no book matches.

How to check it worked

  • The console has no key warning.
  • Typing in the filter updates the list at once.
  • The sort button does not change the order of the original data.

Summary

  • Use array.map(item => <Element key={item.id} />) to show lists.
  • Keys must be unique, stable and come from your data. Avoid random keys, and avoid the index for changing lists.
  • Filter and sort a copy. Keep the full list in state, and calculate what to show.
  • Add, remove and change items by making new arrays with spread, filter and map.

Frequently asked questions

Why does React need keys?

Keys let React tell which item is which between draws, so it can update, move or remove the right items and keep their state correct.

Can I use the array index as the key?

Only for a list that never changes order, and never has items added or removed in the middle. Otherwise use a stable id from your data.

Why can I not read props.key?

Key is used by React itself and is not passed to your component. Pass the value again under another name if you need it.

How do I show nothing for an empty list?

Check the length. If it is zero, show a friendly message instead of an empty list.