Introduction
Welcome back! In our previous lesson, you mastered handling user events, enabling you to build components that respond to clicks and input. You now have the tools to create a single, interactive element. But what happens when you need a whole grid of products, a list of comments, or a dynamic navigation menu? Manually coding each one would be repetitive and impossible to maintain.
This lesson tackles that exact problem. We will explore one of the most fundamental patterns in React: rendering lists of components from a data array. You will learn to use JavaScript's powerful .map() method to transform data into a dynamic UI. This skill is the bridge between building individual components and creating complex, data-driven applications—a core competency for any Design Engineer.
From Static Repetition to Dynamic Rendering
Imagine you're designing a product listing page in Figma. You'd create one product card component and then create instances of it, overriding the data for each one. Writing code manually is like drawing each card from scratch—inefficient and error-prone.
Let's look at what this inefficient approach looks like in code:
function ProductList() {
return (
<div>
<h2>Our Products</h2>
<div>
<h3>Laptop</h3>
<p>Price: $999</p>
</div>
<div>
<h3>Phone</h3>
<p>Price: $699</p>
</div>
<div>
<h3>Tablet</h3>
<p>Price: $499</p>
</div>
</div>
);
}
The structure for each product (<div>, <h3>, <p>) is identical. The only thing that changes is the data. This is a clear signal that we can automate this process. The modern web development solution involves two key steps:
- Store the data in a structured format, like a JavaScript array of objects.
- Use a function to loop through the data and generate a UI element for each item.
In React, that function is .map().
Transforming Data into Components with .map()
The .map() method is a standard JavaScript array function. It creates a new array by calling a function on every element in the original array. In our case, we'll transform an array of data objects into an array of JSX elements that React can render.
Here's the process:
- Define your data: Create an array of objects. Each object represents one item in the list.
- Map over the data: Inside your component's JSX, use
{}to enter JavaScript mode. Call.map()on your data array. - Return JSX: The function you pass to
.map()will receive each item (e.g.,product) and should return the JSX for that item.
// 1. Define the data
const products = [
{ id: 1, name: 'Laptop', price: 999 },
{ id: 2, name: 'Phone', price: 699 },
{ id: 3, name: 'Tablet', price: 499 }
];
function ProductList() {
// 2. Map over the data and 3. Return JSX
const listItems = products.map(product =>
// For each 'product' in 'products', create this li
<li>
<h3>{product.name}</h3>
<p>Price: ${product.price}</p>
</li>
);
return (
<div>
<h2>Our Products</h2>
<ul>{listItems}</ul>
</div>
);
}
The official React documentation offers a concise explanation of this process.
This guide from the React team clearly explains the fundamental pattern of storing data in an array and using .map() to transform it into a list of JSX elements.
Read the introduction and the first section, Rendering data from arrays. This will solidify your understanding of the basic three-step process: move data to an array, map over it to create JSX nodes, and return the result.
The video below offers a great step-by-step walkthrough, starting with a simple array of strings and progressing to an array of objects, which is the more common and useful pattern.
How to render LISTS in React 📃
This video from Bro Code provides a practical demonstration of rendering lists, which can help reinforce the concept visually.
Watch the first two parts: The initial example shows how to transform a simple array of strings into a list. The main segment moves to a more realistic array of objects and introduces the critical concept of the key prop, which we'll discuss next.
The key Prop: A Unique Identifier for Each Item
If you run the code from the previous example, you'll see a warning in your browser's console: Warning: Each child in a list should have a unique "key" prop.
React uses this key prop to identify which list item is which. Think of it like a layer name in Figma or a file name on your desktop. Without a unique identifier, if you reorder, add, or remove items, React gets confused about which item is which and may have to inefficiently re-render the entire list. With a stable key, React knows exactly which element was moved, added, or deleted, and can make precise updates.
Rules for Keys:
- Keys must be unique among siblings. The key for the first product can't be the same as the key for the second product in the same list.
- Keys must be stable. They should not change between re-renders. This is why you should never use an item's array index or a random number (
Math.random()) as a key.
The best source for a key is a unique ID from your data, like a database ID.
Here is the corrected code with the key prop added:
const listItems = products.map(product =>
// The key goes on the top-level element returned by map()
<li key={product.id}>
<h3>{product.name}</h3>
<p>Price: ${product.price}</p>
</li>
);

It is crucial to place the key on the element that is directly inside the .map() call. A common mistake is to place it on an element inside a child component.

The React documentation provides an excellent explanation for why keys are necessary and the rules you must follow when using them.
Please read the section "Keeping list items in order with key". Pay special attention to the analogy about files on a desktop and the "Pitfall" section explaining why using the array index as a key can lead to bugs.
UX Application: Rendering Skeleton Loaders
A fantastic, practical use of list rendering is to improve the user experience during data fetching. Instead of showing a blank screen, you can display a "skeleton" version of the UI. This gives the user a sense of what the content will look like and makes the application feel faster.
To do this, you can create an array of a fixed size and map over it to render several instances of a skeleton component.
// A simple skeleton component using Tailwind CSS for styling
const SkeletonCard = () => (
<div className="border border-gray-200 rounded-md p-4 max-w-sm w-full mx-auto">
<div className="animate-pulse flex space-x-4">
<div className="rounded-full bg-gray-300 h-10 w-10"></div>
<div className="flex-1 space-y-6 py-1">
<div className="h-2 bg-gray-300 rounded"></div>
<div className="space-y-3">
<div className="grid grid-cols-3 gap-4">
<div className="h-2 bg-gray-300 rounded col-span-2"></div>
<div className="h-2 bg-gray-300 rounded col-span-1"></div>
</div>
<div className="h-2 bg-gray-300 rounded"></div>
</div>
</div>
</div>
</div>
);
// Component that shows skeletons while loading
function UserList() {
const isLoading = true; // In a real app, this would come from state
if (isLoading) {
// Create an array of 5 'null' items and map over it
return (
<div>
{Array.from({ length: 5 }).map((_, index) => (
<SkeletonCard key={index} />
))}
</div>
);
}
// ... render actual user data when not loading
}
This technique is a hallmark of a polished, professional UI. The following article provides a great example of this in action.
Creating Beautiful Skeleton Loaders with React and TailwindCSS
This article demonstrates how to combine simple skeleton components into a full layout to create a convincing loading state.
Focus on the section "Some Tips For Creating Layouts". Note how the author uses new Array(5).fill(null).map((_, i) => ...) to render a grid of five skeleton cards. This is the exact pattern for creating a list-based loading state.
Supervising AI: Prompting for List Rendering
When using Cursor, being specific about data structures and iteration is key to getting the code you want.
- Weak Prompt: "Create a list of user profiles."
- Strong, Supervisable Prompt: "Create a React component named
UserGrid. It receives a prop namedusers, which is an array of user objects. Each object has these properties:id(a unique number),name(a string), andavatarUrl(a string). Inside the component, use the.map()method to iterate over theusersarray. For eachuserobject, render adivcontaining animgfor theavatarUrland anh3for thename. Crucially, assign theuser.idto thekeyprop on the rootdivfor each item in the map."
When you review the AI-generated code, you are supervising for these specific points:
- Correct Method: Did it use the
.map()method on the correct array? keyProp Presence: Is there akeyprop on the top-level element inside the map's return statement?keyProp Value: Is the key's value a unique and stable identifier from the data (e.g.,user.id), and not the array index orMath.random()?- Data Access: Are the properties of the object (e.g.,
user.name) accessed correctly within the JSX?
Mastering this supervisory loop is central to your journey as a Design Engineer.
Conclusion
Today you've unlocked a core React pattern that you will use constantly. You've moved from creating single components to dynamically generating entire collections of them from data. This is the foundation for building almost any real-world user interface.
Key Takeaways:
- Avoid manually writing repetitive JSX. Instead, store your data in an array of objects.
- Use the JavaScript
.map()method to transform your data array into an array of JSX elements. - Always provide a stable and unique
keyprop to the root element inside the.map()callback to help React efficiently update the list. The best key is a unique ID from your data. - You can apply this pattern to create sophisticated UX like skeleton loading screens.
In our next lesson, we will explore conditional rendering. You'll learn how to show or hide UI elements based on state or props. This will allow you to do things like display a "No items found" message when a list is empty or toggle the visibility of a dropdown menu—adding another layer of interactivity to your components.