← Back to Articles         
Rendering Lists & Loops in React

Rendering Lists & Loops in React

Published on 11 Nov 2026 21 min read React
React Fundamentals

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 for loop 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 JSX
  • filter() → select which items should be rendered
  • for / for...of → complex iteration logic when necessary
  • forEach() → side effects or manual accumulation, not the normal choice for JSX rendering
  • Object.keys() → render object property names
  • Object.values() → render object values
  • Object.entries() → render object keys and values
  • Array.from() → generate fixed ranges or collections
  • reduce() → advanced accumulation/transformation
  • key → give React a stable identity for each list item

The most important React rule to remember is:

When rendering a collection, use a stable key for each sibling item, and prefer map() when converting array data into JSX.

← Back to Articles