All posts
ReactSeptember 8, 20267 min read

How to Render a List in React: map() & key Prop Guide

Learn how to render a list in React using the map() method and the key prop, step by step, with simple copy-paste examples built for absolute beginners.

F
Fepiq Team
Fepiq

If you searched for how to render a list in React, here is the short answer: you use the JavaScript array method .map() to turn each item in an array into a piece of JSX, and you give each item a unique "key" prop so React can track it. That's it. In this guide we will build that up step by step, with small examples you can copy and run, until you understand exactly why lists work this way in React.

What Does "Rendering a List" Mean in React?

In a React app, your data usually lives in an array. For example, a list of to-do items, blog posts, or products. "Rendering a list" means taking that array of data and showing it on the screen as HTML elements, like a bunch of <li> items or cards. React does not have a special "list" tag for this. Instead, you write plain JavaScript that turns your array into an array of JSX elements, and React displays them.

A Quick Refresher: What Is .map()?

Before we touch React, let's look at .map() as plain JavaScript. It is an array method that creates a new array by running a function on every item of an existing array.

js
const numbers = [1, 2, 3];

const doubled = numbers.map(function (n) {
  return n * 2;
});

console.log(doubled); // [2, 4, 6]

Line by line: we start with the array numbers. We call .map() on it and pass a function. React (well, JavaScript) runs that function once for every item, and n is the current item on each run. The function returns n * 2, so .map() collects all those returned values into a brand new array called doubled. Important: .map() never changes the original array, it always returns a new one.

Step 1: Turn an Array Into JSX With .map()

Now let's do the same thing, but instead of returning a number, we return a piece of JSX (React's HTML-like syntax) for each item.

jsx
function FruitList() {
  const fruits = ['Apple', 'Banana', 'Mango'];

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

Here is what's happening: fruits is a plain array of three strings. Inside the JSX, curly braces { } let us drop back into normal JavaScript, so {fruits.map(...)} runs our map function right there in the markup. For each fruit, we return an <li> element containing that fruit's name. The result is an array of three <li> elements, and React renders them all inside the <ul>. Notice the key={fruit} part on the <li> — we'll explain that next.

Step 2: Why React Needs a key Prop

If you remove the key prop from the example above, your list will still show up on the page, but React will print a warning in the browser console: "Warning: Each child in a list should have a unique 'key' prop." Here's why it matters. When your list changes — an item is added, removed, or reordered — React needs a fast way to know which on-screen element matches which piece of data. The key is that identifier. Without stable keys, React can only guess by position in the array, which can mix up which item is which, especially when items are added or removed from the middle of the list.

jsx
// Avoid: no key at all
fruits.map((fruit) => <li>{fruit}</li>);

// Avoid: key is not stable/unique (index changes if the list is reordered)
fruits.map((fruit, index) => <li key={index}>{fruit}</li>);

// Good: key is a stable, unique value from your data
fruits.map((fruit) => <li key={fruit}>{fruit}</li>);

The first line has no key, so React falls back to warnings and less reliable updates. The second line uses the array index as the key — this works for lists that never reorder or change, but it can cause bugs (like wrong items being edited) if items are added, removed, or shuffled. The third line uses the fruit name itself, which is fine here because every fruit in this small list is unique. In real apps, the best key is usually a unique ID that already exists in your data, like a database ID.

Step 3: Rendering a List of Objects (a Realistic Example)

Most real data isn't a list of plain strings — it's a list of objects, each with an id, a name, and other fields. Here is a common pattern: rendering a list of users.

jsx
function UserList() {
  const users = [
    { id: 1, name: 'Amara', role: 'Designer' },
    { id: 2, name: 'Chen', role: 'Developer' },
    { id: 3, name: 'Diego', role: 'Product Manager' },
  ];

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>
          <strong>{user.name}</strong> — {user.role}
        </li>
      ))}
    </ul>
  );
}

users is now an array of objects instead of strings. Inside .map(), the parameter user represents one object on each pass, so user.name and user.role read that object's fields. We use user.id as the key, because id is unique and stable for each user — it won't change even if the array gets sorted or filtered later. This id-as-key pattern is the standard approach you'll see in almost every real React codebase.

Choosing a Good key: Do's and Don'ts

SituationRecommendation
Data has a unique id from a database or APIUse that id as the key (best option)
Data is a short, fixed list of unique stringsThe string itself can work as a key
List items can be reordered, filtered, or deletedNever use the array index as the key
A static list that never changes orderUsing the index is acceptable, but an id is still safer
Generating a random key each renderAvoid this — it defeats the purpose of keys entirely

Putting It Together: A Small To-Do List

Let's combine list rendering with the useState hook so you can see .map() used inside a real, working component.

jsx
import { useState } from 'react';

function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: 'Learn useState', done: true },
    { id: 2, text: 'Learn map() and key', done: false },
    { id: 3, text: 'Build a small project', done: false },
  ]);

  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>
          {todo.done ? '✅' : '⬜'} {todo.text}
        </li>
      ))}
    </ul>
  );
}

We store the todos array in state using useState, so React re-renders the list whenever todos changes (for example, if you later add a button that marks a todo as done). Inside the JSX, {todos.map(...)} runs on every render, turning the current state into a fresh list of <li> elements. Each <li> uses todo.id as its key, and shows a checkmark or empty box depending on todo.done. If you're new to useState, our guide on the React useState hook covers exactly how that piece works.

Common Mistakes Beginners Make

  • Forgetting the key prop entirely, which causes a console warning and less reliable re-renders.
  • Putting the key on the wrong element — it must go on the outermost element returned inside .map(), not on a child inside it.
  • Using Math.random() as a key — this creates a brand new key on every render, so React thinks every item is new and re-creates it constantly.
  • Trying to loop with a for loop directly inside JSX — JSX only accepts expressions inside { }, so build the array first (usually with .map()) and then insert it.
  • Mutating the array directly (like todos.push(...)) instead of creating a new array with setTodos — React won't detect the change and the screen won't update.

Frequently asked questions

Why does React need a key prop for lists?+

React uses the key to match each list item to the same item in the next render, so it can update, reorder, or remove elements efficiently instead of re-creating everything from scratch. Without a stable key, React can lose track of which item is which, especially when the list changes.

Can I use the array index as the key in React?+

You can, and it works fine for lists that never reorder, filter, or have items removed. But if the list can change, index-based keys can cause React to mix up items, leading to bugs like the wrong item showing as selected or edited. Prefer a unique id from your data whenever one is available.

What's the difference between map() and forEach() for rendering lists?+

map() returns a new array, which is exactly what JSX needs to render — you put {array.map(...)} directly in your markup. forEach() runs a function on each item too, but it returns undefined, so it can't be used to build JSX. Always use map() when rendering lists in React.

Why is my list not updating when I add a new item?+

This usually happens when you mutate the array directly, like todos.push(newTodo), instead of creating a new array. React only re-renders when it sees a new array reference from setState, so use something like setTodos([...todos, newTodo]) to add an item the correct way.

Do I need a unique key for every single list, even a short one?+

Yes. Even a two-item list needs a key on each element, or React will warn you in the console. It only takes a small amount of extra code, and it prevents subtle bugs later if the list grows or changes.

Want to see how state and lists work together in more detail? Read our beginner's guide to the React useState hook next.

Read the useState guide

Rendering lists is one of the most common things you'll do in React, so it's worth practicing until .map() and key feel automatic. Try changing the FruitList or TodoList examples above — add an item, remove one, or sort the array — and watch how the key prop keeps everything matched up correctly.

Need help building a React application for your business? Our team can design, build, and ship it for you.

Talk to us about your project

Get new posts in your inbox

Occasional, no-fluff notes on shipping modern software — startups, automation, Laravel, Shopify and more. No spam, unsubscribe anytime.

Keep reading

Related posts

All posts

Let's build something

Ready to ship your next product with Fepiq?

Book a free discovery call. We'll listen, ask sharp questions, and send you a proposal within 3 business days.