By WebNest Studio Engineering Team
React Tutorial
Rendering Lists with map() and Keys
React does not have a special loop syntax for rendering repeated UI; instead, you use the standard JavaScript array method .map() to transform an array of data into an array of JSX elements, which React can then render directly as siblings.
Whenever you render a list of elements this way, each element needs a special key prop — a stable, unique string or number identifying that item among its siblings. Keys are not visible in the rendered output; they exist purely to help React's reconciliation algorithm track which items changed, were added, or were removed between renders.
Why it matters
Most screens show a list: products, messages, search results. Rendering one takes a single line of React, and the key that goes with it is one of the most misunderstood details. A wrong key gives bugs that look impossible, such as text typed into one row jumping to another.
Transforming Data into JSX with map()
Given an array of data, array.map(item => <li key={item.id}>{item.name}</li>) produces a new array of <li> elements, one per data item, which can be placed directly inside a parent element like <ul>.
Why Keys Matter for Reconciliation
When state or props change, React compares the new list of elements to the previous one to figure out the minimal set of DOM changes needed. Keys let React match up elements across renders by identity rather than by position, so if an item is removed from the middle of a list, React can correctly remove just that one DOM node and reuse the rest — preserving component state, focus, and input values that belong to the untouched items. Without stable keys (or when using the array index as a key on a list that can reorder), React can misattribute state between items, causing subtle bugs like text staying in the wrong input field after a reorder.
Choosing a Good Key
A key should come from the data itself — a database ID, a unique slug, or another value that stays constant across renders and re-orderings for a given logical item. Using the array index as a key is only safe when the list is static, never reordered, and never filtered.
- Keys only need to be unique among sibling elements, not globally unique across the whole app.
- Keys are used internally by React and are not passed down to the component as a prop — access other identifying data through a normal prop if the component needs it.
- Avoid generating a new random key on every render (e.g. Math.random()); this defeats the purpose entirely by making every item look "new" each time.
Example
Rendering a dynamic to-do list with stable keys from item IDs
const todos = [
{ id: 'a1', text: 'Buy groceries' },
{ id: 'a2', text: 'Walk the dog' },
{ id: 'a3', text: 'Write React notes' },
];
function TodoList({ items }) {
return (
<ul>
{items.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
export default function App() {
return <TodoList items={todos} />;
}
Renders an unordered list with three list items reading "Buy groceries", "Walk the dog", and "Write React notes" in order, each keyed by its stable id (a1, a2, a3) so React can track them correctly if the list changes later.
Common Mistakes
- Omitting the key prop entirely, which causes React to log a console warning and fall back to less reliable positional matching.
- Using the array index as a key for a list that can be reordered, filtered, or have items inserted in the middle, which can cause state to attach to the wrong item after a change.
- Generating a brand-new key value on every render (such as Math.random() or Date.now()) instead of a stable identifier, which forces React to treat every item as newly created each time.
- Placing the key prop on a wrapper element other than the actual top-level element returned inside the map() callback.
Exercise
Do this in src/App.jsx of a Vite + React project. Render the tasks as an unordered list, giving each item a stable key. Add a Remove button to each item that removes that task from state.
Expected result: three tasks are listed; clicking Remove beside one deletes only that task, and there is no key warning in the console.
import { useState } from "react";
export default function App() {
const [tasks, setTasks] = useState([
{ id: 1, title: "Learn JSX" },
{ id: 2, title: "Learn state" },
{ id: 3, title: "Build a project" },
]);
// TODO: function remove(id) that keeps every task except the one with that id
return (
<ul>
{/* TODO: one li per task, with a key, the title and a Remove button */}
</ul>
);
}
Show hints
- Use
tasks.map(...)and putkey={task.id}on the outermost element returned for each item. - Remove with
setTasks(tasks.filter((task) => task.id !== id)), which creates a new array.
Show solution
import { useState } from "react";
export default function App() {
const [tasks, setTasks] = useState([
{ id: 1, title: "Learn JSX" },
{ id: 2, title: "Learn state" },
{ id: 3, title: "Build a project" },
]);
function remove(id) {
setTasks(tasks.filter((task) => task.id !== id));
}
return (
<ul>
{tasks.map((task) => (
<li key={task.id}>
{task.title} <button onClick={() => remove(task.id)}>Remove</button>
</li>
))}
</ul>
);
}
Quick quiz
1. What is the purpose of the key prop?
- A. To style the item
- B. To let React identify each item between renders
- C. To sort the list
- D. To pass data to the child
Show answer
B. With keys, React can tell which items were added, removed or moved.
2. What is the best value to use as a key?
- A. A stable, unique id from the data
- B. The array index
- C. Math.random()
- D. The current time
Show answer
A. A random key changes on every render, which destroys and recreates every item.
3. When is using the array index as a key a problem?
- A. Always
- B. Never
- C. Only for lists of numbers
- D. When items can be reordered, inserted or removed
Show answer
D. The index then refers to a different item, and state attached to a row moves to the wrong one.
Interview questions
Why does React need keys?
Show answer
When a list re-renders, React has to match the new elements with the previous ones to decide what to update. Without keys it matches by position, so inserting an item at the top makes every item look changed. With a stable key on each item, React recognises the same item wherever it has moved to, keeps its DOM node and state, and makes only the necessary changes. Keys must be unique among siblings.
Why is the index a poor key for a dynamic list?
Show answer
An index identifies a position, not an item. If the list is sorted, filtered, or has an item inserted or removed, the same index now belongs to a different item. React then reuses the component at that position for the wrong data, so its state, such as the text in an input or a checked box, appears on the wrong row. The index is acceptable only for a static list that never changes order.
Key Points to Remember
- Use array.map() to transform data arrays into arrays of JSX elements for rendering lists.
- Every element in a rendered list needs a unique, stable key prop among its siblings.
- Keys let React's reconciliation correctly match, update, add, or remove list items instead of guessing based on position.
- Prefer a stable ID from the data over the array index, especially for lists that reorder or change.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Shows a live preview of plain HTML, CSS and JavaScript. JSX needs a React project on your computer. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
These examples need a project environment on your computer, so they can be copied but not run here. Codelab runs Python, FastAPI backends with SQLite, plain Java 17 and HTML/CSS/JavaScript.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering