Rendering Lists & Loops in React
Rendering lists is one of the most common tasks in React. Real-world applications rarely display only one fixed element. Usually, data comes from an API, database, JSON object, state, or props, and React needs to generate UI elements dynamically based on that data.
For example, an application may need to display:
- A list of users
- Products
- Employees
- Orders
- Blog articles
- Categories
- Menu items
- Notifications
- Comments
- Table rows
- Cards
- Dropdown options
Instead of manually writing JSX for every item, React allows you to iterate over a collection and generate JSX dynamically.
The most commonly used approach is the JavaScript map() method, but React list rendering can be implemented using several other techniques depending on the requirement.
What Does Rendering a List Mean?
Suppose you have the following data:
const users = ["Syed", "John", "David"];
You want the browser to display:
Syed
John
David
You could manually write:
<h3>Syed</h3>
<h3>John</h3>
<h3>David</h3>
But this is not practical when the data is dynamic.
Instead, React allows you to transform the data into JSX:
const users = ["Syed", "John", "David"];
export const UserList = () => {
return (
<div>
{users.map(user => (
<h3>{user}</h3>
))}
</div>
);
};
Conceptually, React performs the equivalent of:
Array
↓
map()
↓
JSX for each item
↓
React elements
↓
DOM
So if the array contains 100 users, the same JSX structure can generate 100 UI elements.
Why Do We Need List Rendering?
Consider an e-commerce application.
You may receive:
const products = [
{ id: 1, name: "Laptop", price: 75000 },
{ id: 2, name: "Mobile", price: 35000 },
{ id: 3, name: "Keyboard", price: 2000 }
];
The number of products may change.
Today:
3 products
Tomorrow:
50 products
Next month:
500 products
You should not write JSX manually for every product.
Instead:
{products.map(product => (
<div>
<h3>{product.name}</h3>
<p>₹{product.price}</p>
</div>
))}
The UI is therefore data-driven.
The data determines how many elements React creates.
The map() Method — Most Common Approach
The most important technique for rendering lists in React is JavaScript's map() method.
const names = ["Syed", "John", "David"];
export const Users = () => {
return (
<div>
{names.map(name => (
<p>{name}</p>
))}
</div>
);
};
The important concept is:
names
↓
map()
↓
one JSX element for each name
If the array contains:
["Syed", "John", "David"]
React generates the equivalent conceptual structure:
<p>Syed</p>
<p>John</p>
<p>David</p>
The advantage is that the JSX is written only once.
Rendering Objects Using map()
Most real applications work with objects rather than simple strings.
For example:
const users = [
{
id: 1,
name: "Syed",
role: "Developer"
},
{
id: 2,
name: "John",
role: "Designer"
},
{
id: 3,
name: "David",
role: "Manager"
}
];
You can render them like this:
export const Users = () => {
return (
<div>
{users.map(user => (
<div key={user.id}>
<h3>{user.name}</h3>
<p>{user.role}</p>
</div>
))}
</div>
);
};
The result is conceptually:
Syed
Developer
John
Designer
David
Manager
This pattern is extremely common when displaying API data.
Why Is key Required?
When rendering a list, React expects each sibling item to have a unique key.
For example:
{users.map(user => (
<div key={user.id}>
<h3>{user.name}</h3>
</div>
))}
The key helps React identify individual items when the list changes.
Suppose the original list is:
Syed
John
David
Later:
Syed
David
React needs to determine which item was removed.
The key provides that identity.
A good key is usually a stable unique database ID:
key={user.id}
Avoid using unstable values such as:
key={Math.random()}
because the key changes every render.
Rendering a List of Components
List rendering becomes especially useful when each item is represented by a reusable component.
For example:
type User = {
id: number;
name: string;
role: string;
};
A reusable component:
const UserCard = ({ name, role }: User) => {
return (
<div>
<h3>{name}</h3>
<p>{role}</p>
</div>
);
};
Then:
const users: User[] = [
{ id: 1, name: "Syed", role: "Developer" },
{ id: 2, name: "John", role: "Designer" },
{ id: 3, name: "David", role: "Manager" }
];
export const Users = () => {
return (
<div>
{users.map(user => (
<UserCard
key={user.id}
name={user.name}
role={user.role}
/>
))}
</div>
);
};
This is a very important React pattern:
Data
↓
map()
↓
Reusable Component
↓
UI
For larger applications, this approach keeps the code organized and reusable.
Rendering Lists with Index
The map() callback provides the current index as the second parameter.
const names = ["Syed", "John", "David"];
export const Users = () => {
return (
<ul>
{names.map((name, index) => (
<li key={index}>
{index + 1}. {name}
</li>
))}
</ul>
);
};
The output is:
1. Syed
2. John
3. David
The callback receives:
item
index
array
For example:
names.map((name, index) => ...)
Here:
name → current value
index → current position
Should You Use Index as the Key?
Technically:
key={index}
works.
But it is not always recommended.
For static lists where items never change position, it may be acceptable.
For example:
const menuItems = ["Home", "About", "Contact"];
Using:
key={index}
may be reasonable if the list is truly static.
However, for dynamic lists, prefer:
key={item.id}
because the index represents a position, not the identity of the item.
For example, if:
A
B
C
becomes:
A
C
the indexes change, but the IDs remain stable.
Rendering a List Using for Loop
A common question is:
Can I use a normal JavaScript
forloop directly inside JSX?
You cannot directly place a normal for statement inside JSX like this:
return (
<div>
for (let i = 0; i < users.length; i++) {
...
}
</div>
);
This is invalid JSX.
The reason is that for is a JavaScript statement, while the {} section of JSX expects an expression.
However, you can use a for loop before the return statement.
const users = ["Syed", "John", "David"];
export const Users = () => {
const elements = [];
for (let i = 0; i < users.length; i++) {
elements.push(
<h3 key={i}>{users[i]}</h3>
);
}
return (
<div>
{elements}
</div>
);
};
The functionality is:
users
↓
for loop
↓
create JSX elements
↓
store elements in array
↓
render array
This works, but map() is generally cleaner and more idiomatic for React list rendering.
Rendering with for...of
You can also use JavaScript's for...of.
const users = ["Syed", "John", "David"];
export const Users = () => {
const elements = [];
for (const user of users) {
elements.push(
<h3 key={user}>{user}</h3>
);
}
return (
<div>
{elements}
</div>
);
};
This is useful when you need more complex logic while constructing the result.
For example:
for (const user of users) {
if (user === "John") {
continue;
}
elements.push(
<h3 key={user}>{user}</h3>
);
}
The for...of loop gives you greater control over the iteration process.
Rendering with forEach()
You can also use forEach() to construct an array of JSX.
const users = ["Syed", "John", "David"];
export const Users = () => {
const elements: React.ReactNode[] = [];
users.forEach(user => {
elements.push(
<h3 key={user}>{user}</h3>
);
});
return (
<div>
{elements}
</div>
);
};
However, there is an important difference between map() and forEach().
map()
map() creates and returns a new array.
const result = users.map(user => (
<h3 key={user}>{user}</h3>
));
forEach()
forEach() does not return the transformed array.
const result = users.forEach(user => {
// process item
});
result will be:
undefined
Therefore, for normal React list rendering, map() is usually preferred.
Rendering Lists with filter() and map()
Often you don't want to display every item.
For example:
const users = [
{ id: 1, name: "Syed", active: true },
{ id: 2, name: "John", active: false },
{ id: 3, name: "David", active: true }
];
Suppose you want to display only active users.
You can combine:
filter()
+
map()
{users
.filter(user => user.active)
.map(user => (
<div key={user.id}>
{user.name}
</div>
))}
The process is:
All users
↓
filter()
↓
Active users
↓
map()
↓
JSX
↓
UI
This is one of the most common patterns in React.
Rendering Lists with filter(), map() and Conditional Rendering
You can combine multiple techniques.
{users
.filter(user => user.active)
.map(user => (
<div key={user.id}>
<h3>{user.name}</h3>
<p>Active User</p>
</div>
))}
You can also use a conditional expression inside map():
{users.map(user => (
user.active ? (
<div key={user.id}>
{user.name}
</div>
) : null
))}
Both approaches are valid.
However, if you want to completely remove inactive users from the processing chain, filter() followed by map() is often cleaner.
Rendering Different UI Based on Data
List rendering can also generate different UI depending on the item's properties.
const products = [
{ id: 1, name: "Laptop", stock: 10 },
{ id: 2, name: "Mobile", stock: 0 },
{ id: 3, name: "Keyboard", stock: 5 }
];
You can render:
{products.map(product => (
<div key={product.id}>
<h3>{product.name}</h3>
{product.stock > 0 ? (
<p>Available</p>
) : (
<p>Out of Stock</p>
)}
</div>
))}
The important concept is that list rendering and conditional rendering can be combined.
Rendering Lists Using Logical AND (&&)
Another common approach is:
{products.map(product => (
<div key={product.id}>
<h3>{product.name}</h3>
{product.stock > 0 && (
<p>Available</p>
)}
</div>
))}
Here:
product.stock > 0 && (...)
means:
If stock > 0
render the JSX
otherwise
render nothing
This is useful when you only need to render something when a condition is true.
Rendering an Empty List
Suppose:
const users: User[] = [];
Using:
{users.map(user => (
<div key={user.id}>
{user.name}
</div>
))}
does not produce an error.
It simply renders nothing.
However, a good application often displays an empty-state message.
{users.length > 0 ? (
users.map(user => (
<div key={user.id}>
{user.name}
</div>
))
) : (
<p>No users found.</p>
)}
This gives a better user experience.
The logic becomes:
Users exist?
↓
YES → render users
NO → display "No users found"
Rendering a List with Loading State
Real applications frequently retrieve data from an API.
During the API request, you may have:
const [loading, setLoading] = useState(true);
const [users, setUsers] = useState<User[]>([]);
Then:
return (
<div>
{loading ? (
<p>Loading...</p>
) : users.length > 0 ? (
users.map(user => (
<div key={user.id}>
<h3>{user.name}</h3>
</div>
))
) : (
<p>No users found.</p>
)}
</div>
);
This represents a typical real-world state flow:
API request
↓
Loading
↓
API completed
↓
┌───────────────┐
│ │
Data exists No data
│ │
↓ ↓
Render list Empty message
Rendering Lists from API Data
This is probably the most important real-world use case.
Suppose an API returns:
[
{
"id": 1,
"name": "Syed",
"role": "Developer"
},
{
"id": 2,
"name": "John",
"role": "Designer"
}
]
You can store the response in state:
const [users, setUsers] = useState<User[]>([]);
After retrieving the API response:
setUsers(response.data);
Then render:
{users.map(user => (
<div key={user.id}>
<h3>{user.name}</h3>
<p>{user.role}</p>
</div>
))}
The complete architecture becomes:
Database
↓
Backend API
↓
Axios / Fetch
↓
React State
↓
map()
↓
JSX
↓
Browser UI
This is the fundamental pattern behind many React applications.
Rendering Lists Using Object.keys()
Sometimes the data is an object rather than an array.
For example:
const user = {
name: "Syed",
role: "Developer",
experience: 19
};
You can get its property names using:
Object.keys(user)
Then render them:
<ul>
{Object.keys(user).map(key => (
<li key={key}>
{key}: {user[key as keyof typeof user]}
</li>
))}
</ul>
Conceptually:
Object
↓
Object.keys()
↓
["name", "role", "experience"]
↓
map()
↓
UI
This is useful when you need to dynamically display object properties.
Rendering Lists Using Object.values()
Instead of getting property names, you can get the values:
const user = {
name: "Syed",
role: "Developer",
experience: 19
};
Then:
<ul>
{Object.values(user).map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
The result is conceptually:
Syed
Developer
19
This is useful when you only care about the values and not the property names.
Rendering Lists Using Object.entries()
If you need both property name and value:
const user = {
name: "Syed",
role: "Developer",
experience: 19
};
Use:
Object.entries(user)
Then:
<ul>
{Object.entries(user).map(([key, value]) => (
<li key={key}>
{key}: {value}
</li>
))}
</ul>
This is particularly useful for dynamic key-value displays.
Rendering Nested Lists
Sometimes your data contains arrays inside arrays or objects.
For example:
const departments = [
{
id: 1,
name: "Development",
employees: ["Syed", "John"]
},
{
id: 2,
name: "Testing",
employees: ["David", "Alex"]
}
];
You can render both levels:
{departments.map(department => (
<div key={department.id}>
<h3>{department.name}</h3>
<ul>
{department.employees.map(employee => (
<li key={employee}>
{employee}
</li>
))}
</ul>
</div>
))}
The structure is:
Department
↓
map departments
↓
Department UI
↓
Employees
↓
map employees
↓
Employee UI
This is called nested list rendering.
Rendering a Table Using map()
List rendering is not limited to cards and <ul> elements.
You can generate table rows dynamically.
const employees = [
{ id: 1, name: "Syed", role: "Developer" },
{ id: 2, name: "John", role: "Tester" },
{ id: 3, name: "David", role: "Manager" }
];
Render:
<table>
<thead>
<tr>
<th>Name</th>
<th>Role</th>
</tr>
</thead>
<tbody>
{employees.map(employee => (
<tr key={employee.id}>
<td>{employee.name}</td>
<td>{employee.role}</td>
</tr>
))}
</tbody>
</table>
Here, only the <tr> elements are dynamically generated.
This pattern is extremely common in admin dashboards.
Rendering Dropdown Options
List rendering can also be used for <select> options.
const countries = [
{ id: 1, name: "India" },
{ id: 2, name: "USA" },
{ id: 3, name: "UK" }
];
Then:
<select>
{countries.map(country => (
<option key={country.id} value={country.id}>
{country.name}
</option>
))}
</select>
The result is:
India
USA
UK
This is another very common API-driven rendering scenario.
Rendering Navigation Menus
You can store menu configuration as data:
const menuItems = [
{ id: 1, label: "Home", url: "/" },
{ id: 2, label: "About", url: "/about" },
{ id: 3, label: "Contact", url: "/contact" }
];
Then:
<nav>
{menuItems.map(item => (
<a key={item.id} href={item.url}>
{item.label}
</a>
))}
</nav>
This is better than hardcoding every menu item because the menu can later be generated from configuration, permissions, or API data.
Rendering Lists with Fragments
Sometimes you need to return multiple elements for each item.
For example:
{users.map(user => (
<>
<h3>{user.name}</h3>
<p>{user.role}</p>
</>
))}
When rendering multiple elements per list item, the Fragment itself needs a key.
Use:
{users.map(user => (
<React.Fragment key={user.id}>
<h3>{user.name}</h3>
<p>{user.role}</p>
</React.Fragment>
))}
This is preferable to unnecessarily adding an extra <div> just to satisfy JSX's single-parent requirement.
Rendering Lists with a Short Fragment
You can use:
<>
...
</>
for fragments.
However, the short syntax does not allow you to specify a key.
Therefore, this is not suitable when the Fragment itself is the list item:
{users.map(user => (
<>
<h3>{user.name}</h3>
<p>{user.role}</p>
</>
))}
Instead use:
{users.map(user => (
<React.Fragment key={user.id}>
<h3>{user.name}</h3>
<p>{user.role}</p>
</React.Fragment>
))}
Rendering Lists with a Function
You can also move list-generation logic into a function.
const users = ["Syed", "John", "David"];
const renderUsers = () => {
return users.map(user => (
<li key={user}>{user}</li>
));
};
export const Users = () => {
return (
<ul>
{renderUsers()}
</ul>
);
};
This can be useful when rendering logic becomes complicated.
However, don't create unnecessary rendering functions for simple lists.
For simple cases:
{users.map(user => (
<li key={user}>{user}</li>
))}
is easier to understand.
Rendering Lists Using a Separate Component
For larger applications, you can move the list into its own component.
type User = {
id: number;
name: string;
};
type UserListProps = {
users: User[];
};
export const UserList = ({ users }: UserListProps) => {
return (
<div>
{users.map(user => (
<div key={user.id}>
{user.name}
</div>
))}
</div>
);
};
Then the parent component can provide the data:
<UserList users={users} />
This creates a clean separation:
Parent Component
↓
Provides data through props
↓
UserList
↓
map()
↓
User UI
Rendering Lists with Sorting
You can sort data before rendering.
const users = [
{ id: 1, name: "David" },
{ id: 2, name: "Syed" },
{ id: 3, name: "John" }
];
Then:
{[...users]
.sort((a, b) => a.name.localeCompare(b.name))
.map(user => (
<div key={user.id}>
{user.name}
</div>
))}
The spread operator:
[...users]
creates a copy before sorting.
This avoids directly mutating the original array.
The processing becomes:
Original data
↓
Copy array
↓
Sort
↓
map()
↓
Render
Rendering Lists with Pagination
For large datasets, you may not want to render every item simultaneously.
For example, if an API returns 1,000 records, you may display only 10 records per page.
Conceptually:
const startIndex = (page - 1) * pageSize;
const currentItems = users.slice(
startIndex,
startIndex + pageSize
);
Then:
{currentItems.map(user => (
<div key={user.id}>
{user.name}
</div>
))}
The architecture becomes:
1000 records
↓
Pagination logic
↓
10 records
↓
map()
↓
UI
This is commonly used in data grids and admin applications.
Rendering Lists with Search
Suppose the user searches for:
syed
You can filter first:
const filteredUsers = users.filter(user =>
user.name.toLowerCase().includes(searchTerm.toLowerCase())
);
Then:
{filteredUsers.map(user => (
<div key={user.id}>
{user.name}
</div>
))}
The flow becomes:
Users
↓
Search text
↓
filter()
↓
Matching users
↓
map()
↓
UI
This is a very common pattern for search boxes.
Rendering Lists with Conditional Styling
You can also dynamically apply CSS classes based on item data.
{products.map(product => (
<div
key={product.id}
className={product.stock === 0 ? "out-of-stock" : "available"}
>
<h3>{product.name}</h3>
</div>
))}
Here the list determines not only what gets rendered, but also how each item looks.
For example:
Laptop
Available
Mobile
Out of Stock
Rendering Lists with Dynamic Inline Styles
The same principle works with inline styles.
{products.map(product => (
<div
key={product.id}
style={{
opacity: product.stock === 0 ? 0.5 : 1
}}
>
{product.name}
</div>
))}
Each rendered item receives a style based on its data.
Rendering a List with reduce()
Although map() is the standard choice, reduce() can also produce JSX.
For example:
const names = ["Syed", "John", "David"];
const elements = names.reduce<React.ReactNode[]>(
(result, name) => {
result.push(
<li key={name}>{name}</li>
);
return result;
},
[]
);
Then:
<ul>
{elements}
</ul>
However, reduce() is generally unnecessary for simple list rendering.
Use:
map()
when your requirement is simply:
one item → one JSX element
Use reduce() when you genuinely need accumulation or transformation involving multiple items.
Rendering Lists from a Range
Sometimes you need to generate a fixed number of elements.
For example, displaying five stars:
{Array.from({ length: 5 }).map((_, index) => (
<span key={index}>★</span>
))}
This produces:
★★★★★
The important part is:
Array.from({ length: 5 })
which creates an iterable array-like structure containing five positions.
Then:
map()
generates one element for each position.
Rendering a Number Range
You can create a range:
const numbers = Array.from(
{ length: 10 },
(_, index) => index + 1
);
Then:
{numbers.map(number => (
<span key={number}>
{number}
</span>
))}
This can be useful for:
- Pagination buttons
- Star ratings
- Calendar days
- Numeric selectors
- Step indicators
Rendering Nested Components
Consider a shopping cart:
const cart = [
{
id: 1,
name: "Laptop",
quantity: 2
},
{
id: 2,
name: "Mouse",
quantity: 3
}
];
You could render:
{cart.map(item => (
<div key={item.id}>
<h3>{item.name}</h3>
<p>Quantity: {item.quantity}</p>
</div>
))}
The list is not limited to simple text. Each iteration can produce a complete component tree.
Conceptually:
Cart
↓
map()
↓
Cart Item
├── Product Name
├── Quantity
└── Other controls
Rendering Lists with Event Handlers
A very common requirement is to perform an action for the specific item the user clicks.
For example:
const users = [
{ id: 1, name: "Syed" },
{ id: 2, name: "John" },
{ id: 3, name: "David" }
];
const handleDelete = (id: number) => {
console.log("Delete user:", id);
};
Render:
{users.map(user => (
<div key={user.id}>
<span>{user.name}</span>
<button onClick={() => handleDelete(user.id)}>
Delete
</button>
</div>
))}
Each button is associated with its corresponding user.
For example:
Syed → Delete → id 1
John → Delete → id 2
David → Delete → id 3
This pattern is fundamental for CRUD applications.
Rendering Lists with State Updates
Suppose you want to delete an item.
const [users, setUsers] = useState([
{ id: 1, name: "Syed" },
{ id: 2, name: "John" },
{ id: 3, name: "David" }
]);
Delete functionality:
const handleDelete = (id: number) => {
setUsers(users =>
users.filter(user => user.id !== id)
);
};
Then render:
{users.map(user => (
<div key={user.id}>
<span>{user.name}</span>
<button onClick={() => handleDelete(user.id)}>
Delete
</button>
</div>
))}
The flow is:
User clicks Delete
↓
User ID captured
↓
filter()
↓
New array created
↓
setUsers()
↓
Component re-renders
↓
Updated list displayed
This demonstrates why list rendering and state management are closely connected.
Rendering Lists with key — Important Rules
The key should generally be:
- Unique among sibling elements
- Stable across renders
- Associated with the identity of the item
Good:
key={user.id}
Usually acceptable for a static list:
key={index}
Avoid:
key={Math.random()}
Avoid using mutable values that can change unexpectedly:
key={user.name}
if names are not guaranteed to be unique and stable.
The ideal key is usually the database/API identifier.
key Is Not Passed as a Normal Prop
This is an important React behavior.
Consider:
<UserCard
key={user.id}
id={user.id}
name={user.name}
/>
Inside UserCard, you cannot access:
props.key
because key is a special React property.
If the component needs the ID, pass it separately:
<UserCard
key={user.id}
id={user.id}
name={user.name}
/>
Then:
const UserCard = ({ id, name }: UserProps) => {
// id is available
};
The distinction is:
key
↓
React's internal list reconciliation
id
↓
Your component's data
Common Mistake — Missing key
This:
{users.map(user => (
<div>
{user.name}
</div>
))}
may produce a React warning about keys.
Correct:
{users.map(user => (
<div key={user.id}>
{user.name}
</div>
))}
The key belongs on the outermost element returned by the map callback.
For example:
{users.map(user => (
<div key={user.id}>
<h3>{user.name}</h3>
<p>{user.role}</p>
</div>
))}
Not:
{users.map(user => (
<div>
<h3 key={user.id}>{user.name}</h3>
<p>{user.role}</p>
</div>
))}
The list item itself needs the key.
Common Mistake — Using forEach() Instead of map()
This is incorrect:
const users = ["Syed", "John", "David"];
const elements = users.forEach(user => (
<h3>{user}</h3>
));
forEach() doesn't return the generated array.
For normal list transformation, use:
const elements = users.map(user => (
<h3 key={user}>{user}</h3>
));
Common Mistake — Mutating the Original Array
Avoid directly modifying state arrays.
For example:
users.push(newUser);
This mutates the existing array.
Instead, create a new array:
setUsers(prevUsers => [
...prevUsers,
newUser
]);
Similarly, instead of:
users.splice(index, 1);
use:
setUsers(prevUsers =>
prevUsers.filter(user => user.id !== id)
);
React state management generally relies on immutable updates.
map() vs for vs forEach() vs for...of
| Technique | Returns new array | Direct JSX expression | Typical React use |
|---|---|---|---|
map() |
Yes | Yes | Best for list rendering |
forEach() |
No | No | Side effects / manual accumulation |
for |
No | No | Complex iteration |
for...of |
No | No | Complex iteration |
filter() |
Yes | Yes | Select items before rendering |
reduce() |
Yes | Yes | Advanced accumulation/transformation |
For most React list rendering:
map()
should be your first choice.
The Most Common List Rendering Pattern
For day-to-day React development, this pattern is extremely important:
{items.map(item => (
<Component
key={item.id}
{...item}
/>
))}
For example:
{products.map(product => (
<ProductCard
key={product.id}
name={product.name}
price={product.price}
image={product.image}
/>
))}
This gives a clean separation between:
Data
↓
List rendering
↓
Reusable component
↓
UI
Complete Real-World Example
Consider a product listing page.
type Product = {
id: number;
name: string;
price: number;
stock: number;
};
const products: Product[] = [
{
id: 1,
name: "Laptop",
price: 75000,
stock: 5
},
{
id: 2,
name: "Mobile",
price: 35000,
stock: 0
},
{
id: 3,
name: "Keyboard",
price: 2000,
stock: 10
}
];
export const ProductList = () => {
return (
<div>
<h2>Products</h2>
{products.length > 0 ? (
products.map(product => (
<div key={product.id}>
<h3>{product.name}</h3>
<p>
Price: ₹{product.price}
</p>
{product.stock > 0 ? (
<p>
Available: {product.stock}
</p>
) : (
<p>
Out of Stock
</p>
)}
</div>
))
) : (
<p>No products available.</p>
)}
</div>
);
};
This single example combines several important React concepts:
Array
↓
Check whether data exists
↓
map()
↓
Create UI for every product
↓
Use key for identity
↓
Conditional rendering based on stock
↓
Display product information
This is very close to how list rendering works in production React applications.
Rendering Lists — Overall Mental Model
The easiest way to understand React list rendering is:
DATA
│
▼
ARRAY
│
▼
filter()
│
▼
map()
│
▼
JSX / COMPONENT
│
▼
React Tree
│
▼
Browser UI
You don't normally write a separate JSX block for every record.
Instead, you define:
"How should one item look?"
Then map() applies that definition to every item.
For example:
users.map(user => (
<UserCard key={user.id} user={user} />
))
means:
For every user in this collection, create one
UserCard.
That is the core idea behind Rendering Lists & Loops in React.
Recommended Approach
For most React applications, use the following decision pattern:
Need to render an array?
│
▼
map()
│
▼
Need to remove/select items first?
│
Yes
│
▼
filter()
│
▼
map()
For example:
{users
.filter(user => user.active)
.map(user => (
<UserCard
key={user.id}
user={user}
/>
))}
Use:
map()→ transform each item into JSXfilter()→ select which items should be renderedfor/for...of→ complex iteration logic when necessaryforEach()→ side effects or manual accumulation, not the normal choice for JSX renderingObject.keys()→ render object property namesObject.values()→ render object valuesObject.entries()→ render object keys and valuesArray.from()→ generate fixed ranges or collectionsreduce()→ advanced accumulation/transformationkey→ give React a stable identity for each list item
The most important React rule to remember is:
When rendering a collection, use a stable
keyfor each sibling item, and prefermap()when converting array data into JSX.