Topic 3.2
Rendering Lists with map
In one line
To show a list, keep the data in an array and turn every item into an element with map; use filter first to choose which items appear, and move the look of one item into its own component.
Think of it like this
A cookie cutter. You don't carve every cookie by hand. You make one cutter and press it into the dough once per cookie. In React, the cutter is a small piece of JSX (or a component) and the dough is your array of data: map presses the cutter once for every item and gives you a tray of cookies, an array of elements.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Array
- An ordered list of values, written in square brackets:
["a", "b", "c"]. - map
- An array method that calls a function on every item and returns a new array of the results.
- filter
- An array method that returns a new array with only the items that pass a test.
- Array of objects
- A list where every item is an object with fields, like
[{ id: 1, name: "Asha" }, ...]. - Derived data
- Values you can calculate from other data (like the filtered list), so you compute them during render instead of storing them.
- List item component
- A small component that draws one item of a list, used inside
map. - forEach
- An array method that runs a function for every item but returns nothing (
undefined). Not useful inside JSX.
Step by step
01Keep the data in an array
Start with the data, not the markup. Each object is one book; the id is a unique label for it.
const books = [
{ id: "b1", title: "Malgudi Days", pages: 256 },
{ id: "b2", title: "The God of Small Things", pages: 340 },
{ id: "b3", title: "Wings of Fire", pages: 196 },
];02Describe one item, map over all of them
Write what one book looks like, and let map repeat it. The result is an array of three <li> elements, which JSX renders in order.
function BookList() {
return (
<ul>
{books.map((book) => (
<li key={book.id}>{book.title} ({book.pages} pages)</li>
))}
</ul>
);
}03Filter first to choose which items show
Chain filter before map. Nothing is stored: the shorter list is worked out from the full list on every render.
const short = books.filter((b) => b.pages < 300);
return <ul>{short.map((b) => <li key={b.id}>{b.title}</li>)}</ul>;04Move one item into its own component
When an item has more than a line of JSX, give it a component. The list passes each item as a prop and puts the key on the component element.
function BookRow({ book }) {
return (
<li>
<strong>{book.title}</strong> <small>{book.pages} pages</small>
</li>
);
}
function BookList({ books }) {
return <ul>{books.map((b) => <BookRow key={b.id} book={b} />)}</ul>;
}05The list follows the data
Add an object to the array (or let the user add one through state) and the next render maps over four items instead of three. You never write "insert a new <li>". This is UI = f(state) from Topic 0.1 applied to lists.
Try it yourself
- 1
Add a book
In the first example, add a fourth book object with
id: "b4". Predict the console line before running. (map made 4 elements.) - 2
Add a sort
In the fruit shop, add
.toSorted((a, b) => a.price - b.price)after the two filters. The list now goes from cheapest to dearest, andfruitsitself is untouched becausetoSortedreturns a copy. - 3
Show an empty message
Type
kiwiin the fruit search: the list is empty and the page looks broken. Add{visible.length === 0 && <p>No fruit matches.</p>}under the count. (Topic 3.4 covers empty states properly.) - 4
Destructure in the parameter
In the team list, change
PersonCard({ person })toPersonCard({ person: { name, role, online } })and update the JSX to usename,roleandonline. Same page, shorter JSX.
Code & diagrams
`items` is an ordinary array of React elements. Putting an array inside `<ul>{...}</ul>` renders every element in order.
Preview
Console
Nothing logged yet.Expected console output
map made 3 elementsType `an` or tick **Under ₹100**. The visible list is calculated from the full list and the two inputs on every render; it is never stored.
Preview
Console
Nothing logged yet.`TeamList` decides which people and in what order; `PersonCard` decides how one person looks. Change the card once and every row changes.
Preview
Console
Nothing logged yet.Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Use forEach instead of map
In the first example, change books.map( to books.forEach(.
Break #2
Render a whole object
In the team list, replace <strong>{person.name}</strong> with <strong>{person}</strong>.
Myth vs fact
Myth
React has a special loop syntax for lists.
Fact
There is no v-for or *ngFor. You use JavaScript's map, which returns an array, and JSX renders arrays.
Myth
You should store the filtered list in state.
Fact
A filtered list can always be calculated from the full list and the filter values, so compute it during render. Storing it as well means two copies that can disagree.
Myth
A for loop can't be used in React.
Fact
It can, outside the JSX: push elements into an array in a for loop, then render the array. map is preferred because it is an expression and fits inside JSX.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
JSX children can be arrays, and arrays can be nested:
<ul>{[a, [b, c]]}</ul>renders all three. React flattens nested arrays when rendering, but each array is its own keyed group for reconciliation. - ▸
filter+mapin render is O(n) on every render. For a few hundred items it is negligible; when profiling shows a slow calculation, cache it withuseMemo(Topic 16.4) or let the React Compiler do it (Topic 16.5). Don't move it into state plus an effect: that adds an extra render and a sync bug. - ▸
toSorted,toReversedandtoSpliced(ES2023) return copies, which makes sorting inside render safe. Callingitems.sort()on a prop or state array mutates it in place: a bug that is easy to miss because the screen still looks right (Topic 4.7). - ▸
Extracting an item component also gives React a natural place to skip work later: with
memo, unchanged rows can skip rendering when one row changes (Topic 16.3).
Remember this
- 1
In real apps lists come from data: products from a server, messages, search results. You don't write
<li>ten times by hand. You keep the items in an array (an ordered list of values) and describe how *one* item looks. When the array changes (a product is added, a message arrives) the list on screen follows automatically, because it is computed from the array on every render. - 2
**
mapturns data into elements.**array.map(fn)callsfnonce for every item and returns a new array of whateverfnreturned. In JSX that is an array of elements:books.map((book) => <li key={book.id}>{book.title}</li>). JSX can render arrays directly, so<ul>{items}</ul>shows every element in it, in order.mapdoesn't change the original array, which matters because React data should never be changed in place (Topic 4.7). - 3
Arrays of objects are the usual shape of list data:
{ id: "b1", title: "Malgudi Days", pages: 256 }. Inside the arrow function you read the fields you need (book.title), or destructure them:books.map(({ id, title }) => <li key={id}>{title}</li>). Each item may show several fields, a conditional class, nested elements, anything a normal piece of JSX can do. Thekeyyou see on each item is explained fully in Topic 3.3; for now, always give it the item'sid. - 4
**
filterthenmap.** To show only some items, chainfilterbeforemap:fruits.filter((f) => f.price < 100).map((f) => <li key={f.id}>{f.name}</li>).filter(fn)returns a new array with only the items for whichfnreturnstrue. Keep derived data like this as a plain calculation during render, not in its own state: computevisiblefromfruitsandqueryeach render, so it can never fall out of step (Topic 6.2). You can also sort (copy first:[...items].sort(...)oritems.toSorted(...)) orslicebefore mapping. - 5
Watch the arrow function's body.
(x) => <li>{x}</li>returns the element.(x) => { <li>{x}</li> }with curly braces is a block with noreturn, so every item becomesundefinedand the list is empty with no error. And usemap, notforEach:forEachalways returnsundefined, so{items.forEach(...)}renders nothing. - 6
Extract a list item component. When one item's JSX grows (an avatar, a name, a role, a status dot), move it into its own component:
function PersonCard({ person }) { ... }, thenpeople.map((p) => <PersonCard key={p.id} person={p} />). The list component handles *which* items and *in what order*; the item component handles *how one looks*. Thekeystays in themap, on the<PersonCard>element, not insidePersonCard. Very long lists (thousands of rows) need extra techniques such as virtualisation (Topic 16.9).
Explain it without notes
How do you render a list in React? Why map and not forEach or a for loop inside JSX?
Why should a filtered list be calculated during render rather than stored in state?
When should you extract a list item into its own component, and where does the key go then?
A list renders nothing and the console is empty. What are the usual causes?
Practice
Given const cities = ["Delhi", "Mumbai", "Chennai"], render them as an ordered list (<ol>).
Given an array of { id, name, inStock } products, render only the ones in stock, each as <li> with the name.
Extract a ScoreRow component that shows a player's name and score, and a ScoreBoard that renders an array of { id, name, score } with it.
Trade-offs
- ↔
Computing filtered lists in render keeps one source of truth at the cost of repeating the work every render; that cost only matters for big lists or slow filters, where
useMemofixes it. - ↔
Extracting an item component adds a file or a function, but keeps the list readable and lets each row be styled, tested and optimised on its own.
Done when you can
I can render an array of objects with
map.I can filter (and sort a copy) before mapping.
I calculate filtered lists during render instead of storing them.
I can extract a list item component and put the key on it in the
map.I can spot the missing
returnandforEachmistakes.