Web Development with React
Hands-onBeginner 30 min· React core · 8 of 21

Lists, keys and conditional rendering

Turn arrays of data into lists of components with map(), understand why every item needs a stable key, and show or hide parts of the screen with conditional rendering. You will build a filterable Brew & Bean menu.

In this tutorial you will build a menu page for Brew & Bean, a small coffee shop in Indiranagar, Bengaluru. The page shows a list of items, lets the customer filter by category, and shows a friendly message when nothing matches.

Prerequisites: a Vite + React project (see *Start a React project with Vite*), and you know components, props and useState (see *Components and JSX*, *Props and composition* and *State and events*). Plain JavaScript (JSX) is used throughout.

Rendering a list with map()

Real data usually arrives as an array: a menu, a list of orders, search results. React has no special loop syntax. Instead you use the JavaScript array method map(), which turns each item of an array into something else. Here, each menu object becomes a piece of JSX.

Think of a kitchen printer: for every order slip (data) it prints one ticket (UI). map() is the printer.

src/menuData.js
export const MENU = [
  { id: 'espresso', name: 'Espresso', category: 'coffee', price: 120 },
  { id: 'cappuccino', name: 'Cappuccino', category: 'coffee', price: 180 },
  { id: 'cold-brew', name: 'Cold Brew', category: 'coffee', price: 220 },
  { id: 'filter-coffee', name: 'Filter Coffee', category: 'coffee', price: 80 },
  { id: 'masala-chai', name: 'Masala Chai', category: 'tea', price: 90 },
  { id: 'croissant', name: 'Butter Croissant', category: 'bakery', price: 150 },
  { id: 'banana-bread', name: 'Banana Bread', category: 'bakery', price: 130 },
  { id: 'veg-sandwich', name: 'Veg Grilled Sandwich', category: 'food', price: 160 },
];

Our sample data. Create this file first; every later step imports it.

src/App.jsx
import { MENU } from './menuData';

function App() {
  return (
    <main>
      <h1>Brew & Bean menu</h1>
      <ul>
        {MENU.map((item) => (
          <li key={item.id}>
            {item.name} - ₹{item.price}
          </li>
        ))}
      </ul>
    </main>
  );
}

export default App;

The curly braces drop into JavaScript. map() returns an array of <li> elements and React renders them in order.

What you should see
A heading, then eight bullet points such as "Espresso - ₹120" and "Masala Chai - ₹90".

Notice the arrow function uses round brackets ( ... ) after =>, not curly braces. Round brackets return the JSX directly. If you write { ... } you must add the word return, or you will get an empty list and no error. This is the most common beginner slip.

Extract a component

A list item usually grows: a price, a badge, a button. Move it into its own component so App stays readable. The key stays on the element you create inside map(), which is now <MenuItem />, not inside the component.

src/MenuItem.jsx
function MenuItem({ name, price, category }) {
  return (
    <li>
      <strong>{name}</strong> ({category}) - ₹{price}
    </li>
  );
}

export default MenuItem;
src/App.jsx (updated list)
import { MENU } from './menuData';
import MenuItem from './MenuItem';

function App() {
  return (
    <main>
      <h1>Brew & Bean menu</h1>
      <ul>
        {MENU.map((item) => (
          <MenuItem
            key={item.id}
            name={item.name}
            price={item.price}
            category={item.category}
          />
        ))}
      </ul>
    </main>
  );
}

export default App;

key is used by React itself. MenuItem never receives it as a prop.

Why keys matter

If you left the key out, the page would still render but the browser console would warn: "Each child in a list should have a unique key prop". A key is the item's identity. When the list changes (an item is added, removed or reordered), React compares old keys with new keys to decide which elements to keep, move or delete.

Without stable keys React matches items by position. Insert one item at the top and every other item looks "changed", so React may reuse the wrong DOM element. You then see odd bugs: a text box keeps the typed text of a different row, or an animation plays on the wrong item.

Choosing a key

KeyGood?Why
item.id from your data or databaseYesUnique and does not change when the list changes
A slug such as 'cold-brew'YesUnique and stable, as long as no two items share it
The array index (map((item, i) => ... key={i}))Only for static listsChanges when you insert, remove, sort or filter, which causes the bugs above
Math.random() or crypto.randomUUID() inside renderNeverA new key on every render forces React to destroy and recreate every item

Keys must be unique among siblings, not globally

Two different lists can both use the id espresso. But two items in the same list must not. Duplicate keys cause the same confusing bugs as missing keys. If your data has no id, create one when the data is created (for example when the user clicks Add), not while rendering.

Conditional rendering

Conditional rendering means showing different UI depending on data or state. There is no special syntax: you use ordinary JavaScript inside JSX. Three patterns cover almost everything.

Three patterns

PatternCodeUse when
if / early returnif (!items.length) return <p>Empty</p>;A whole component should show something else
Ternary{isOpen ? <Open /> : <Closed />}Choose between two pieces of UI
Logical AND{hasOffer && <Badge />}Show something or nothing

The 0 trap with &&

{count && <Badge />} renders the number 0 on screen when count is 0, because 0 is a valid thing for React to print. Make the condition a real boolean: {count > 0 && <Badge />}. false, null and undefined render nothing, but 0 and NaN do print.

Build the filter

Now combine both ideas. We keep the selected category in state, derive the filtered list from it, and render a message when the filtered list is empty. Do not store the filtered list in state: it can always be calculated from MENU and the selected category, so storing it only creates a chance for the two to disagree.

Add a category filter

  1. 1Import useState from React in App.jsx and create const [category, setCategory] = useState('all');.
  2. 2Build the list of buttons from the data: const categories = ['all', ...new Set(MENU.map((item) => item.category))];. The Set removes duplicates; the spread turns it back into an array.
  3. 3Calculate visibleItems with filter(): all items when category is 'all', otherwise only items whose category matches.
  4. 4Render one button per category with map(). Use the category name as the key and add a CSS class to the selected one with a ternary.
  5. 5Render visibleItems as before, but show 'Nothing here yet' when the array is empty.
src/App.jsx (final)
import { useState } from 'react';
import { MENU } from './menuData';
import MenuItem from './MenuItem';

function App() {
  const [category, setCategory] = useState('all');

  const categories = ['all', ...new Set(MENU.map((item) => item.category))];

  const visibleItems =
    category === 'all'
      ? MENU
      : MENU.filter((item) => item.category === category);

  return (
    <main>
      <h1>Brew & Bean menu</h1>

      <div>
        {categories.map((c) => (
          <button
            key={c}
            onClick={() => setCategory(c)}
            style={{ fontWeight: c === category ? 'bold' : 'normal' }}
          >
            {c}
          </button>
        ))}
      </div>

      {visibleItems.length === 0 ? (
        <p>Nothing here yet.</p>
      ) : (
        <ul>
          {visibleItems.map((item) => (
            <MenuItem
              key={item.id}
              name={item.name}
              price={item.price}
              category={item.category}
            />
          ))}
        </ul>
      )}
    </main>
  );
}

export default App;

Clicking a button updates state, React re-runs App, the filter is recalculated and the list is redrawn. The selected button is bold.

What you should see
Five buttons: all, coffee, tea, bakery and food. Clicking `bakery` shows only Butter Croissant and Banana Bread. Clicking `tea` shows only Masala Chai.

Seeing a key bug for yourself

Temporarily change key={item.id} to key={Math.random()} and open the browser console and React DevTools highlight-updates option: every item is rebuilt on every click. Switch back to item.id. Seeing the difference once makes the rule stick.

Common mistakes

When your list misbehaves

  1. Did you use ( ) after the arrow, or { } with a return?
  2. Is the key on the outermost element returned by map() (here <MenuItem />)?
  3. Is the key unique and stable (an id, not Math.random and usually not the index)?
  4. Did you mutate the array (push, sort, reverse) that lives in state or props? Copy first: [...items].sort(...).
  5. Are you using && with a number that could be 0?
Prompt · Ask AI to review your list code
I am learning React. Review this component that renders a list. Check the key I chose, the conditional rendering, and anything that could cause bugs when items are added, removed or reordered. Explain each problem in plain English with a one-line fix.

---
[paste your component here]
---

What to expect: A good answer names the exact line, explains why index or random keys cause bugs, and does not rewrite the whole file.

Try it: Extend the menu

1. Add a caffeine: true or false field to each item in the data, then add a Caffeine-free only checkbox that hides items with caffeine. 2. Show a small Pricey badge next to items costing ₹200 or more, using &&. 3. Add a search text box that filters by name (case-insensitive) together with the category filter. 4. Add a Sort by price button. Sort a copy of the array, not the original.

Key takeaways

  1. Use array.map() to turn data into JSX; remember round brackets after => or an explicit return.
  2. Every element in a list needs a key that is unique among siblings and stable over time, normally an id from your data.
  3. Avoid index keys for lists that change, and never use random keys.
  4. Conditional rendering is plain JavaScript: early return, ternary and &&. Beware 0 && ....
  5. Derive values like a filtered list during render instead of storing them in state.

Check yourself

Finishing the quiz marks the lesson as done.

1. What is the main job of the key prop in a list?

2. You have a list the user can reorder and delete items from. Which key is safest?

3. {count && <Badge />} shows a stray "0" on screen. Why, and what is the fix?

4. Your menu component does items.map((item) => { <li>{item.name}</li> }) and renders nothing. What is wrong?

5. Where is the best place to keep the filtered list of menu items?

Saved on this device. Create a free account to keep it on every device.

Practise this with Sam

A real mock interview, out loud, with an honest scorecard. Free to start.

Start free