Question
How can I render the <ObjectRow /> component multiple times inside a React <tbody>? I would like to produce a row for each value from 0 up to numrows - 1, similar to this template-style pseudocode:
<tbody>
for (let i = 0; i < numrows; i++) {
<ObjectRow />
}
</tbody>
I understand that a for statement cannot be written directly inside JSX. What is the React JSX approach for adding the same component multiple times?
Short Answer
You will learn how React renders lists of components, why statements such as for cannot appear directly inside JSX, and how to create repeated elements with Array.from() or map(). You will also learn why each rendered row needs a stable key.
Concept
JSX describes the UI that a React component should return. Inside JSX braces ({}), you can place JavaScript expressions: values that evaluate to something React can render.
A for loop is a statement, not an expression. It performs work but does not itself produce an array of elements for JSX to render. That is why this is invalid:
<tbody>
{for (let i = 0; i < 3; i++) {}}
</tbody>
React can render an array of elements. Therefore, the common solution is to create an array and transform each item into a component using map().
When you only have a number such as numrows, Array.from() can first create an array of that length. Then map() creates one <ObjectRow /> per index.
Every item in a rendered list needs a key prop. A key lets React identify which rendered item corresponds to which logical item between updates. Stable keys help React update lists correctly and efficiently.
For a fixed number of purely visual rows, an index can be acceptable as a key. For rows backed by real data that can be reordered, inserted, or removed, use a stable ID from that data instead.
Mental Model
Think of JSX as an order form that must contain completed items, not instructions for making them.
- A
forloop is an instruction: “repeat this action.” - An array of
<ObjectRow />elements is the completed set of items: “here are the rows to display.” map()is the assembly line that turns each input item into one UI item.- A
keyis each row's tracking label, allowing React to recognize it during later updates.
JSX can receive the finished array, so React can render every row in it.
Syntax and Examples
Create a sequence from a count with Array.from(), then map each index to a component:
function ObjectTable({ numrows }) {
return (
<table>
<tbody>
{Array.from({ length: numrows }, (_, index) => (
<ObjectRow key={index} rowNumber={index} />
))}
</tbody>
</table>
);
}
Array.from({ length: numrows }, ...) creates an array with numrows positions. Its callback runs once for each position:
indexstarts at0.- The callback returns an
<ObjectRow />element. - React renders the resulting array inside
<tbody>. key={index}gives each generated element a key.
A table row component should return a <tr>, because it is being placed directly inside a :
Step by Step Execution
Consider this component:
function ObjectTable() {
const numrows = 3;
const rows = Array.from({ length: numrows }, (_, index) => (
<ObjectRow key={index} rowNumber={index + 1} />
));
return <tbody>{rows}</tbody>;
}
Execution proceeds as follows:
-
numrowsis set to3. -
Array.from({ length: 3 }, callback)creates three positions. -
The callback runs for index
0and returns:<ObjectRow key={0} rowNumber={1} />
Real World Use Cases
Repeated component rendering is used throughout React applications:
- Database tables: Render one
<UserRow />for every user returned by an API. - Product listings: Render one
<ProductCard />for every product in a search result. - Navigation menus: Render links from a configuration array.
- Form fields: Render repeated input groups, such as addresses or invoice line items.
- Loading placeholders: Render a known number of skeleton rows while data is loading.
- Calendar views: Render days, events, or time slots generated from date data.
For example, skeleton table rows can be count-based:
<tbody>
{isLoading
? Array.from({ length: 5 }, (_, index) => (
<SkeletonRow key={index} />
))
: users.map((user) => (
<UserRow key={user.id} user={user} />
))}
</tbody>
Real Codebase Usage
In real codebases, developers generally make rendering data-driven. Rather than separately storing a count and trying to repeat markup, they store an array of records and map it to components.
function OrdersTable({ orders }) {
if (orders.length === 0) {
return <p>No orders found.</p>;
}
return (
<table>
<tbody>
{orders.map((order) => (
<OrderRow key={order.id} order={order} />
))}
</tbody>
</table>
);
}
Common patterns include:
- Guard clauses: Return an empty state or loading state before rendering the main list.
- Filtering first: Use
filter()beforemap()when only some records should appear. - Sorting first: Create a sorted copy of data before mapping if the display order matters.
- Small row components: Keep table-cell markup inside
OrderRow, while the parent owns list rendering.
Common Mistakes
Writing a for loop directly in JSX
This is invalid because for is a statement:
<tbody>
for (let i = 0; i < numrows; i++) {
<ObjectRow />
}
</tbody>
Create the array before return, or use an expression such as map() inside braces.
Forgetting braces around JavaScript
Without braces, JSX treats the text as content rather than JavaScript:
// Incorrect
<tbody>
Array.from({ length: 3 })
</tbody>
Use braces:
<tbody>{Array.from({ length: 3 })}</tbody>
In practice, also map those positions to elements.
Forgetting the key prop
React will warn when list items have no keys:
Comparisons
| Approach | Best for | Example |
|---|---|---|
array.map() | Rendering records you already have | users.map(user => <UserRow ... />) |
Array.from({ length }) | Rendering a known count with no data array | Skeleton rows, stars, page buttons |
for loop before return | More complex generation logic | Build rows, then render {rows} |
forEach() | Side effects, not creating rendered output | Logging or updating external values |
map() and forEach() are often confused:
Cheat Sheet
// Render from existing data: preferred for application data
items.map((item) => <Item key={item.id} item={item} />)
// Render a component a fixed number of times
Array.from({ length: count }, (_, index) => (
<Item key={index} number={index + 1} />
))
// Prepare elements before return
const elements = [];
for (let index = 0; index < count; index += 1) {
elements.push(<Item key={index} />);
}
return <section>{elements}</section>;
- Put JavaScript expressions in JSX with
{...}. - A
forloop cannot be placed directly in JSX.
FAQ
How do I loop through a number in React JSX?
Use Array.from({ length: count }, callback). The callback can return one React element for each index.
{Array.from({ length: count }, (_, index) => <Item key={index} />)}
Can I use a for loop in a React component?
Yes. Use it in JavaScript before the return statement to build an array of elements, then render that array with {elements}. You cannot write the loop directly as JSX content.
Why does React require a key when using map()?
Keys let React match each list item with its previous rendered version. This matters when items are added, removed, or reordered.
Is key={index} always wrong in React?
No. It is acceptable for static generated items, such as loading placeholders. Avoid it for data lists whose order or length can change; use a stable item ID instead.
Why does my table show invalid DOM warnings?
A <tbody> expects <tr> children. Ensure the component you render inside it returns a , and place cells inside or elements.
Mini Project
Description
Build a small score table that generates a requested number of table rows. Each row displays a row number and an editable default score. This demonstrates count-based component rendering, passing props, and correct table markup.
Goal
Render a <ScoreRow /> component once for every row requested by the user.
Requirements
Create a ScoreRow component that returns a <tr> element.
Render rows inside a <tbody> using Array.from().
Give every generated row a key prop.
Pass the displayed row number as a prop.
Allow the user to choose a row count between 1 and 10.
Keep learning
Related questions
Abort Ajax Requests with jQuery jqXHR.abort()
Learn how to cancel an in-progress jQuery Ajax request with jqXHR.abort(), handle abort status safely, and avoid stale UI updates.
Access the Correct this Inside a JavaScript Callback
Learn why JavaScript this changes in callbacks and how to preserve an object context using bind, arrow functions, and event handler patterns.
Add Key-Value Pairs to JavaScript Objects
Learn how to add key-value pairs to JavaScript objects with dot and bracket notation, dynamic keys, examples, and common mistakes.