Lists and Keys
Most UIs involve lists — income sources, spending categories, transactions. React renders lists by mapping an array to JSX. The only rule: every item needs a key prop.
Rendering a list with map
Section titled “Rendering a list with map”const categories = [ { id: 'housing', name: 'Housing', budgeted: 1200 }, { id: 'food', name: 'Food', budgeted: 600 }, { id: 'transport', name: 'Transportation', budgeted: 400 },];
export default function CategoriesGrid({ categories }) { return ( <div className="categories-grid"> {categories.map(cat => ( <CategoryCard key={cat.id} category={cat} /> ))} </div> );}Array.prototype.map transforms each item in the array into a JSX element. React renders the resulting array of elements.
Why keys are required
Section titled “Why keys are required”When React re-renders a list, it needs to know which items changed, which were added, and which were removed. Without keys, React re-renders the entire list on every change — slow, and it causes bugs with form inputs that lose focus or animations that reset.
Keys let React match each rendered element to the item it came from across re-renders. When an item is removed, React removes only that element. When an item is reordered, React moves the DOM node rather than recreating it.
The key must be unique among siblings — not globally unique. It must be stable — the same item should always get the same key. Using the database ID or a UUID is correct. Using the array index is a last resort (it causes bugs when the order changes).
{items.map((item, index) => ( <li key={item.id}>...</li> ✓ stable, unique))}
{items.map((item, index) => ( <li key={index}>...</li> ✗ breaks when list reorders))}Where the key goes
Section titled “Where the key goes”The key goes on the outermost element returned from map, not on a child inside it:
{categories.map(cat => ( <CategoryCard key={cat.id} category={cat} /> ✓))}
{categories.map(cat => ( <CategoryCard category={cat}> <div key={cat.id}>...</div> ✗ wrong level </CategoryCard>))}The key is not a prop — it is metadata for React. The CategoryCard component cannot read props.key.
Rendering nested lists
Section titled “Rendering nested lists”ZeroBudget has transactions nested inside categories. Map the outer array, then map the inner array:
{categories.map(cat => ( <CategoryCard key={cat.id} category={cat}> {transactions .filter(tx => tx.categoryId === cat.id) .map(tx => ( <TransactionRow key={tx.id} transaction={tx} /> ))} </CategoryCard>))}Filter the transactions for each category before mapping them. This is pure JavaScript — React does not need special syntax for it.
Exercise
Section titled “Exercise”- Create a
CategoriesGridcomponent that accepts acategoriesarray prop and maps it to<CategoryCard>components. - In
CategoryCard, render the category name and budgeted amount from thecategoryprop. - Create a
defaults.jsfile insrc/data/with the 8 ZeroBudget default categories (Housing, Transportation, Food, Utilities, Clothing, Personal, Savings, Giving — each with a uniqueid,name, andbudgeted: 0). - Import the defaults in
App.jsxand pass them toCategoriesGrid. Confirm all 8 cards render.
- Use
Array.prototype.mapto render lists — it returns an array of JSX elements. - Every element returned from
mapneeds a unique, stablekeyprop. - Keys go on the outermost element from
map, not on children inside it. - Use item IDs for keys — not array indexes.
- The
keyprop is metadata for React; the component cannot read it.