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.
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.
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.
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.
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.
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.
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.
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.
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.
// 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.
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.
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.
| Situation | Recommendation |
|---|---|
| Data has a unique id from a database or API | Use that id as the key (best option) |
| Data is a short, fixed list of unique strings | The string itself can work as a key |
| List items can be reordered, filtered, or deleted | Never use the array index as the key |
| A static list that never changes order | Using the index is acceptable, but an id is still safer |
| Generating a random key each render | Avoid this — it defeats the purpose of keys entirely |
Let's combine list rendering with the useState hook so you can see .map() used inside a real, working component.
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.
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.
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.
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.
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.
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 guideRendering 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 projectOccasional, no-fluff notes on shipping modern software — startups, automation, Laravel, Shopify and more. No spam, unsubscribe anytime.
Keep reading
What is an index in SQL? A plain-English guide with copy-paste examples showing how indexes speed up queries and when you actually need one.
Learn JavaScript DOM manipulation for beginners: select elements, change text and styles, and handle clicks with simple, copy-paste code examples.
Let's build something
Book a free discovery call. We'll listen, ask sharp questions, and send you a proposal within 3 business days.