← Back

React.js + Next.js · Module 2. React Basics · Lesson 8

Rendering Collections

React applications often display collections of data. Pizza Palace needs collections for:

  • the pizza menu;
  • toppings and sizes;
  • shopping-cart items;
  • customer orders;
  • search results;
  • kitchen order queues.

Instead of writing every card manually, we store the data in a JavaScript array and transform it into JSX with .map().

You will learn

  1. What collections are in React.
  2. How arrays work with JSX.
  3. How .map() generates elements and components.
  4. How to render arrays of objects.
  5. Why React needs stable and unique key values.
  6. Why array indexes and random values are usually poor keys.
  7. How to type collections with TypeScript.
  8. How to pass collections through props.
  9. How to display an empty collection message.

REACT · 01

1. What Is a Collection?

A collection is a group of values. In JavaScript, collections are commonly stored in arrays.

Array of Numbers

const quantities = [1, 2, 3];

Array of Strings

const pizzaNames = [
  "Margherita",
  "Pepperoni",
  "Vegetarian"
];

Array of Objects

const pizzas = [
  { id: 1, name: "Margherita", price: 8.5 },
  { id: 2, name: "Pepperoni", price: 10.5 },
  { id: 3, name: "Vegetarian", price: 9.5 }
];

Each object describes one pizza. React can transform these objects into menu cards.


REACT · 02

2. Why Do We Need .map()?

We could write every pizza manually:

function App() {
  return (
    <div>
      <p>Margherita</p>
      <p>Pepperoni</p>
      <p>Vegetarian</p>
    </div>
  );
}

export default App;

Browser Result

Margherita

Pepperoni

Vegetarian

The three names appear, but every paragraph was written manually.

This works for three items but becomes difficult for a large or changing menu. With .map(), the array becomes the source of truth and React generates the JSX automatically.


REACT · 03

3. Understanding JavaScript .map()

The .map() method creates a new array by applying a function to every item in an existing array.

const prices = [8, 10, 12];

const pricesWithDelivery = prices.map(function (price) {
  return price + 2;
});

console.log(pricesWithDelivery);

Console Result

[10, 12, 14]

The callback runs once for each price and returns a new value. .map() does not modify the original prices array.

Use .map() when you need a new value for every item. Later, JSX elements will be those new values.


REACT · 04

4. Using .map() in JSX

Place the .map() expression inside JSX curly braces.

const quantities = [1, 2, 3];

function App() {
  return (
    <div>
      {quantities.map(function (quantity) {
        return <p key={quantity}>Quantity: {quantity}</p>;
      })}
    </div>
  );
}

export default App;

Browser Result

Quantity: 1

Quantity: 2

Quantity: 3

Each number becomes one paragraph.

Step by step:

  1. quantities stores the data.
  2. .map() visits every number.
  3. The callback returns one <p> for each number.
  4. React renders the resulting array of JSX elements.

REACT · 05

5. Rendering an Array of Strings

An array of unique pizza names can become a list.

const pizzaNames = [
  "Margherita",
  "Pepperoni",
  "Vegetarian"
];

function App() {
  return (
    <main>
      <h1>Pizza Menu</h1>

      <ul>
        {pizzaNames.map(function (pizzaName) {
          return <li key={pizzaName}>{pizzaName}</li>;
        })}
      </ul>
    </main>
  );
}

export default App;

Browser Result

Pizza Menu

  • Margherita
  • Pepperoni
  • Vegetarian
One list item is generated for every string.

Using a name as a key is safe only when every name is unique and stable. Real menu data should normally have an ID.


REACT · 06

6. Rendering an Array of Objects

Real menu items need more than a name.

const pizzas = [
  { id: 1, name: "Margherita", price: 8.5 },
  { id: 2, name: "Pepperoni", price: 10.5 },
  { id: 3, name: "Vegetarian", price: 9.5 }
];

function App() {
  return (
    <main>
      <h1>Pizza Menu</h1>

      {pizzas.map(function (pizza) {
        return (
          <article key={pizza.id}>
            <h2>{pizza.name}</h2>
            <p>Price: {pizza.price} €</p>
          </article>
        );
      })}
    </main>
  );
}

export default App;

Browser Result

Pizza Menu

Margherita

8.50 €

Pepperoni

10.50 €

Vegetarian

9.50 €

Each object becomes one menu card.

Inside the callback, pizza represents one object. We read pizza.id, pizza.name, and pizza.price with dot notation.


REACT · 07

7. What Is the key Attribute?

React uses the special key attribute to identify siblings in a rendered collection.

<article key={pizza.id}>
  <h2>{pizza.name}</h2>
</article>

Keys help React match old items to new items when a pizza is added, removed, or moved.

A good key is:

  1. unique among siblings in the same collection;
  2. stable between renders;
  3. based on the identity of the data item.

The usual choice is:

key={pizza.id}

key is not passed to a child component as a normal prop. If PizzaCard also needs the ID, pass it separately:

<PizzaCard
  key={pizza.id}
  id={pizza.id}
  name={pizza.name}
/>

REACT · 08

8. Why Avoid Array Indexes as Keys?

Array indexes describe positions, not item identities.

pizzas.map(function (pizza, index) {
  return <PizzaCard key={index} name={pizza.name} />;
})

Imagine the original cart:

Index 0 → Margherita
Index 1 → Pepperoni
Index 2 → Vegetarian

After removing Margherita:

Index 0 → Pepperoni
Index 1 → Vegetarian

The indexes now refer to different pizzas. React may associate existing component state with the wrong item.

Prefer key={pizza.id} when items can be inserted, removed, filtered, or reordered.

An index can be acceptable for a simple static list when items never change and no stable ID exists.


REACT · 09

9. Why Random Keys Are Incorrect

Do not generate a new key during every render.

Incorrect:

<PizzaCard key={Math.random()} name={pizza.name} />

The value changes each time, so React may treat the card as a completely new component. That can cause unnecessary remounting and loss of local state.

Correct:

<PizzaCard key={pizza.id} name={pizza.name} />

Also avoid generating keys from the current time during rendering.


REACT · 10

10. Typing Arrays in TypeScript

Define the structure of one pizza with an interface:

interface Pizza {
  id: number;
  name: string;
  price: number;
  isVegetarian: boolean;
}

Then type the array:

const pizzas: Pizza[] = [
  { id: 1, name: "Margherita", price: 8.5, isVegetarian: true },
  { id: 2, name: "Pepperoni", price: 10.5, isVegetarian: false }
];

Pizza describes one object. Pizza[] means an array containing Pizza objects.

Incorrect:

const pizzas: Pizza[] = [
  { id: 1, name: "Margherita", price: "8.50", isVegetarian: true }
];

TypeScript reports an error because price must be a number, not a string.


REACT · 11

11. Rendering Typed Collections

Combine a TypeScript interface with .map().

interface Pizza {
  id: number;
  name: string;
  price: number;
}

const pizzas: Pizza[] = [
  { id: 1, name: "Margherita", price: 8.5 },
  { id: 2, name: "Pepperoni", price: 10.5 },
  { id: 3, name: "Vegetarian", price: 9.5 }
];

export default function App() {
  return (
    <main>
      <h1>Pizza Menu</h1>

      {pizzas.map(function (pizza) {
        return (
          <article key={pizza.id}>
            <h2>{pizza.name}</h2>
            <p>Price: {pizza.price} €</p>
          </article>
        );
      })}
    </main>
  );
}

Browser Result

Pizza Menu

Margherita

8.50 €

Pepperoni

10.50 €

Vegetarian

9.50 €

TypeScript checks the data before React renders the same three cards.

TypeScript knows that each pizza contains id, name, and price.


REACT · 12

12. Combining .map() with Components

In a real application, create a reusable component for each list item.

PizzaCard.tsx

interface PizzaCardProps {
  name: string;
  price: number;
}

function PizzaCard(props: PizzaCardProps) {
  return (
    <article>
      <h2>{props.name}</h2>
      <p>Price: {props.price} €</p>
    </article>
  );
}

export default PizzaCard;

App.tsx

import PizzaCard from "./components/PizzaCard";

interface Pizza {
  id: number;
  name: string;
  price: number;
}

const pizzas: Pizza[] = [
  { id: 1, name: "Margherita", price: 8.5 },
  { id: 2, name: "Pepperoni", price: 10.5 },
  { id: 3, name: "Vegetarian", price: 9.5 }
];

export default function App() {
  return (
    <main>
      <h1>Pizza Menu</h1>

      {pizzas.map(function (pizza) {
        return (
          <PizzaCard
            key={pizza.id}
            name={pizza.name}
            price={pizza.price}
          />
        );
      })}
    </main>
  );
}

Browser Result

Pizza Menu

Margherita

8.50 €

Pepperoni

10.50 €

Vegetarian

9.50 €

App maps data into three reusable PizzaCard components.

.map() handles repetition, props provide each pizza's data, and PizzaCard owns the presentation.


REACT · 13

13. Passing a Collection Through Props

A parent can store the menu array and pass it to PizzaMenu.

PizzaMenu.tsx

import PizzaCard from "./PizzaCard";

interface Pizza {
  id: number;
  name: string;
  price: number;
}

interface PizzaMenuProps {
  pizzas: Pizza[];
}

function PizzaMenu(props: PizzaMenuProps) {
  return (
    <section>
      {props.pizzas.map(function (pizza) {
        return (
          <PizzaCard
            key={pizza.id}
            name={pizza.name}
            price={pizza.price}
          />
        );
      })}
    </section>
  );
}

export default PizzaMenu;

App.tsx

import PizzaMenu from "./components/PizzaMenu";

const pizzas = [
  { id: 1, name: "Margherita", price: 8.5 },
  { id: 2, name: "Pepperoni", price: 10.5 }
];

export default function App() {
  return (
    <main>
      <h1>Pizza Palace</h1>
      <PizzaMenu pizzas={pizzas} />
    </main>
  );
}

Browser Result

Pizza Palace

Margherita

8.50 €

Pepperoni

10.50 €

App provides the array; PizzaMenu renders it.

This parent-to-child collection pattern is common in React applications.


REACT · 14

14. Rendering Empty Collections

An empty array produces no list items. A helpful message gives customers a clearer result.

interface Pizza {
  id: number;
  name: string;
}

const pizzas: Pizza[] = [];

export default function App() {
  return (
    <main>
      <h1>Pizza Menu</h1>

      {pizzas.length === 0 ? (
        <p>No pizzas are currently available.</p>
      ) : (
        <ul>
          {pizzas.map(function (pizza) {
            return <li key={pizza.id}>{pizza.name}</li>;
          })}
        </ul>
      )}
    </main>
  );
}

Browser Result

Pizza Menu

No pizzas are currently available.

The empty-state branch replaces an empty list with useful feedback.

This combines the conditional rendering from Lesson 7 with .map().


REACT · 15

15. Practical Application — Pizza Palace Menu

Each menu item needs an ID, name, ingredients, size, price, and availability.

interface Pizza {
  id: number;
  name: string;
  ingredients: string;
  size: "small" | "medium" | "large";
  price: number;
  isAvailable: boolean;
}

const pizzas: Pizza[] = [
  {
    id: 1,
    name: "Margherita",
    ingredients: "Tomato, mozzarella, basil",
    size: "medium",
    price: 8.5,
    isAvailable: true
  },
  {
    id: 2,
    name: "Pepperoni",
    ingredients: "Tomato, mozzarella, pepperoni",
    size: "large",
    price: 12.5,
    isAvailable: true
  },
  {
    id: 3,
    name: "Truffle Pizza",
    ingredients: "Mozzarella, mushrooms, truffle cream",
    size: "medium",
    price: 14,
    isAvailable: false
  }
];

export default function App() {
  return (
    <main>
      <h1>Pizza Palace Menu</h1>

      {pizzas.map(function (pizza) {
        return (
          <article key={pizza.id}>
            <h2>{pizza.name}</h2>
            <p>{pizza.ingredients}</p>
            <p>Size: {pizza.size}</p>
            <p>Price: {pizza.price} €</p>
            <p>{pizza.isAvailable ? "Available" : "Sold out"}</p>
          </article>
        );
      })}
    </main>
  );
}

Browser Result

Pizza Palace Menu

Margherita

Medium · 8.50 €

Available

Pepperoni

Large · 12.50 €

Available

Truffle Pizza

Medium · 14 €

Sold out

One typed array generates the complete menu.

This structure can later come from an API instead of a constant in the file.


REACT · 16

16. Common Beginner Mistakes

Mistake 1 — Forgetting return in a Block Callback

Incorrect:

pizzas.map(function (pizza) {
  <p key={pizza.id}>{pizza.name}</p>;
})

Correct:

pizzas.map(function (pizza) {
  return <p key={pizza.id}>{pizza.name}</p>;
})

Mistake 2 — Forgetting key

Incorrect:

pizzas.map(function (pizza) {
  return <PizzaCard name={pizza.name} />;
})

Correct:

pizzas.map(function (pizza) {
  return <PizzaCard key={pizza.id} name={pizza.name} />;
})

Mistake 3 — Placing key Inside the Child

The key belongs on the element returned directly by .map().

return <PizzaCard key={pizza.id} name={pizza.name} />;

Mistake 4 — Expecting key as a Prop

If the child needs the ID, pass both values:

<PizzaCard key={pizza.id} id={pizza.id} name={pizza.name} />

Mistake 5 — Using forEach Instead of map

.forEach() returns undefined; .map() returns the new array of JSX elements React needs.


REACT · 17

17. Practice Exercises

Exercise 1 — Pizza Names

Create an array with three pizza names and render them as <li> elements with .map().

Exercise 2 — Typed Menu

Create a Pizza interface with id, name, and price. Render three pizza objects as cards.

Exercise 3 — Reusable PizzaCard

Move the card markup into PizzaCard.tsx and pass the pizza data through props.

Exercise 4 — Empty Menu

Display No pizzas are currently available when the array is empty.

Exercise 5 — Stable Keys

Give every pizza a stable ID. Remove one item and confirm that the remaining IDs do not change.

Exercise 6 — Cart Items

Create a typed cart array containing id, pizzaName, quantity, and unitPrice. Render the line total for every item.


REACT · 18

18. Key Takeaways

  • Arrays store collections of values or objects.
  • .map() creates a new value for every array item.
  • JSX can render an array of React elements.
  • Use a stable and unique key for every sibling generated by .map().
  • Database or data-model IDs are usually better keys than indexes.
  • Never generate keys with Math.random() or the current time during rendering.
  • key is special and is not passed as a normal prop.
  • TypeScript interfaces describe collection items, and Pizza[] describes the array.
  • Reusable components separate data from presentation.
  • Empty collections should usually show a helpful message.

REACT · 19

19. Further Reading

Next lesson: Rendering a React application in the DOM with index.html, main.tsx, createRoot(), and the Pizza Palace App component.

← Back↑ Back to the top