dev.log / syntax diaries

Practical code notes, tools, and guided learning for developers.

Practical guides, developer tools, and tutorials for modern web developers, with the same focused tone across writing, utilities, and learning tracks.

BlogToolsTutorialsAboutContactAdmin Login
Privacy PolicyTerms of ServiceCookie Policy

ยฉ 2026 The Syntax Diaries ยท System_Operational

The Syntax Diaries logoThe Syntax Diaries
BlogToolsTutorialsAbout
build log live
Tutorial / React
State And Events30 minbeginner

Lists and Keys

Learn how to render lists of data efficiently and understand why React needs keys for optimal performance.

On This Page

Why Are Lists So Important?From Static to Dynamic: Your First ListStep 1: Start with an ArrayStep 2: Use the map() FunctionStep 3: Add the Key Prop (Important!)Understanding the map() FunctionLet's Build a Real Todo ListThe Mystery of Keys: Why Does React Need Them?The Problem Without KeysThe Solution: Unique KeysRules for Good KeysRule 1: Keys Must Be UniqueRule 2: Keys Should Be StableRule 3: Avoid Array Index When Order Can ChangeDifferent Types of ListsSimple String ArraysObject Arrays (Most Common)Nested ListsAdvanced List OperationsFiltering ListsSorting ListsPractice Exercise: Build a Contact ListCommon List Mistakes and SolutionsMistake 1: Missing KeysMistake 2: Mutating State ArraysMistake 3: Using Index as Key for Dynamic ListsMistake 4: Not Handling Empty ListsPerformance Tips for ListsTip 1: Use Unique, Stable KeysTip 2: Keep List Items SimpleTip 3: Handle Large Lists CarefullyWhat We've LearnedQuick Recap QuizWhat's Next?

Lists and Keys#

Think about any app you use daily - Instagram shows a list of posts, Spotify shows a list of songs, Amazon shows a list of products. Lists are everywhere! Almost every real application needs to display collections of data dynamically.

In this lesson, you'll learn how to turn boring static content into dynamic, interactive lists that can grow, shrink, and change based on user actions.

Why Are Lists So Important?#

Imagine you're building a todo app. You could hardcode each todo item:

function TodoApp() {
  return (
    <ul>
      <li>Buy groceries</li>
      <li>Walk the dog</li>
      <li>Learn React</li>
    </ul>
  );
}

But what happens when users want to:

  • Add new todos?
  • Delete completed todos?
  • Mark todos as done?
  • Have 100 todos instead of 3?

You'd need to manually update your code every time! That's not practical.

Lists solve this problem by letting you display data dynamically from arrays.

From Static to Dynamic: Your First List#

Let's transform a static list into a dynamic one step by step.

Step 1: Start with an Array#

Instead of hardcoding items, let's use an array:

function FruitList() {
  const fruits = ["๐ŸŽ Apple", "๐ŸŒ Banana", "๐ŸŠ Orange"];
  
  return (
    <div>
      <h2>My Favorite Fruits</h2>
      {/* How do we display this array? */}
    </div>
  );
}

Step 2: Use the map() Function#

The map() function is your new best friend! It transforms each item in an array into JSX:

function FruitList() {
  const fruits = ["๐ŸŽ Apple", "๐ŸŒ Banana", "๐ŸŠ Orange"];
  
  return (
    <div>
      <h2>My Favorite Fruits</h2>
      <ul>
        {fruits.map((fruit) => (
          <li>{fruit}</li>
        ))}
      </ul>
    </div>
  );
}

What's happening here?

  1. fruits.map() goes through each fruit in the array
  2. For each fruit, it creates a <li> element
  3. The {} tells React to treat this as JavaScript
  4. React displays all the <li> elements

Step 3: Add the Key Prop (Important!)#

If you try the code above, React will show a warning in the console. Here's the fixed version:

function FruitList() {
  const fruits = ["๐ŸŽ Apple", "๐ŸŒ Banana", "๐ŸŠ Orange"];
  
  return (
    <div>
      <h2>My Favorite Fruits</h2>
      <ul>
        {fruits.map((fruit, index) => (
          <li key={index}>{fruit}</li>
        ))}
      </ul>
    </div>
  );
}

We'll learn more about keys in a moment, but for now, just remember: every list item needs a key prop!

Understanding the map() Function#

Let's break down map() because it's crucial for lists:

// Think of map() like this:
const numbers = [1, 2, 3];

// map() creates a NEW array by transforming each item
const doubledNumbers = numbers.map((number) => number * 2);
// Result: [2, 4, 6]

// In React, we transform data into JSX
const numberElements = numbers.map((number) => <p>{number}</p>);
// Result: [<p>1</p>, <p>2</p>, <p>3</p>]

Key points about map():

  • It doesn't change the original array
  • It returns a new array
  • You provide a function that transforms each item
  • In React, we transform data into JSX elements

Let's Build a Real Todo List#

Now let's create something more practical - a todo list where users can add and remove items:

function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: "Learn React", completed: false },
    { id: 2, text: "Build a project", completed: false },
    { id: 3, text: "Get a job", completed: false }
  ]);
  
  const [newTodo, setNewTodo] = useState("");
  
  const addTodo = () => {
    if (newTodo.trim()) { // Only add if not empty
      const todo = {
        id: Date.now(), // Simple ID generation
        text: newTodo,
        completed: false
      };
      setTodos([...todos, todo]); // Add to the end
      setNewTodo(""); // Clear input
    }
  };
  
  const deleteTodo = (id) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };
  
  const toggleTodo = (id) => {
    setTodos(todos.map(todo => 
      todo.id === id 
        ? { ...todo, completed: !todo.completed }
        : todo
    ));
  };
  
  return (
    <div style={{ padding: "20px", maxWidth: "400px" }}>
      <h2>๐Ÿ“ My Todo List</h2>
      
      {/* Add new todo */}
      <div style={{ marginBottom: "20px" }}>
        <input
          type="text"
          value={newTodo}
          onChange={(e) => setNewTodo(e.target.value)}
          placeholder="Add a new todo..."
          onKeyPress={(e) => e.key === 'Enter' && addTodo()}
          style={{ marginRight: "10px", padding: "5px" }}
        />
        <button onClick={addTodo}>Add</button>
      </div>
      
      {/* Todo list */}
      {todos.length === 0 ? (
        <p>No todos yet! Add one above.</p>
      ) : (
        <ul style={{ listStyle: "none", padding: 0 }}>
          {todos.map(todo => (
            <li 
              key={todo.id}
              style={{
                display: "flex",
                alignItems: "center",
                padding: "10px",
                backgroundColor: todo.completed ? "#d4edda" : "#fff",
                border: "1px solid #ddd",
                borderRadius: "5px",
                marginBottom: "5px"
              }}
            >
              <input
                type="checkbox"
                checked={todo.completed}
                onChange={() => toggleTodo(todo.id)}
                style={{ marginRight: "10px" }}
              />
              
              <span 
                style={{
                  flex: 1,
                  textDecoration: todo.completed ? "line-through" : "none",
                  color: todo.completed ? "#6c757d" : "#000"
                }}
              >
                {todo.text}
              </span>
              
              <button 
                onClick={() => deleteTodo(todo.id)}
                style={{
                  backgroundColor: "#dc3545",
                  color: "white",
                  border: "none",
                  padding: "5px 10px",
                  borderRadius: "3px",
                  cursor: "pointer"
                }}
              >
                Delete
              </button>
            </li>
          ))}
        </ul>
      )}
      
      {/* Summary */}
      <div style={{ marginTop: "20px", fontSize: "14px", color: "#666" }}>
        Total: {todos.length} | 
        Completed: {todos.filter(t => t.completed).length} | 
        Remaining: {todos.filter(t => !t.completed).length}
      </div>
    </div>
  );
}

What makes this example great?

  • โœ… Uses proper keys (todo.id)
  • โœ… Handles empty state
  • โœ… Shows interactive list operations
  • โœ… Demonstrates real-world patterns

The Mystery of Keys: Why Does React Need Them?#

You might wonder: "Why does React care about keys?" Great question! Let me explain with a story.

The Problem Without Keys#

Imagine React is like a librarian trying to keep track of books on a shelf:

// Without keys, React sees this:
<ul>
  <li>Book 1</li>
  <li>Book 2</li>
  <li>Book 3</li>
</ul>

// If you add a book at the beginning:
<ul>
  <li>NEW BOOK</li>  {/* React thinks this was "Book 1" */}
  <li>Book 1</li>     {/* React thinks this was "Book 2" */}
  <li>Book 2</li>     {/* React thinks this was "Book 3" */}
  <li>Book 3</li>     {/* React thinks this is completely new */}
</ul>

React gets confused and might:

  • Re-render everything unnecessarily
  • Lose component state
  • Have poor performance

The Solution: Unique Keys#

With keys, it's like giving each book a barcode:

// With keys, React can track each item precisely:
<ul>
  <li key="new">NEW BOOK</li>    {/* React: "Oh, this is new!" */}
  <li key="book1">Book 1</li>    {/* React: "I know this one, just moved!" */}
  <li key="book2">Book 2</li>    {/* React: "This one moved too!" */}
  <li key="book3">Book 3</li>    {/* React: "And this one!" */}
</ul>

Now React can efficiently update only what changed!

Rules for Good Keys#

Rule 1: Keys Must Be Unique#

// โŒ Bad - duplicate keys
{items.map(item => <li key="same-key">{item}</li>)}

// โœ… Good - unique keys
{items.map(item => <li key={item.id}>{item}</li>)}

Rule 2: Keys Should Be Stable#

// โŒ Bad - keys change every render
{items.map(item => <li key={Math.random()}>{item}</li>)}

// โœ… Good - keys stay the same
{items.map(item => <li key={item.id}>{item}</li>)}

Rule 3: Avoid Array Index When Order Can Change#

// โŒ Problematic when items can be reordered
{items.map((item, index) => <li key={index}>{item}</li>)}

// โœ… Better - use unique property
{items.map(item => <li key={item.id}>{item}</li>)}

When is index okay? When the list never changes order (like a static menu).

Different Types of Lists#

Simple String Arrays#

function ColorList() {
  const colors = ["red", "green", "blue", "yellow"];
  
  return (
    <ul>
      {colors.map((color, index) => (
        <li 
          key={color} // color is unique, so we can use it
          style={{ color: color }}
        >
          {color}
        </li>
      ))}
    </ul>
  );
}

Object Arrays (Most Common)#

function UserList() {
  const users = [
    { id: 1, name: "Alice", email: "alice@example.com", role: "Admin" },
    { id: 2, name: "Bob", email: "bob@example.com", role: "User" },
    { id: 3, name: "Charlie", email: "charlie@example.com", role: "Editor" }
  ];
  
  return (
    <div>
      {users.map(user => (
        <div 
          key={user.id}
          style={{
            border: "1px solid #ccc",
            padding: "10px",
            margin: "10px 0",
            borderRadius: "5px"
          }}
        >
          <h3>{user.name}</h3>
          <p>Email: {user.email}</p>
          <span 
            style={{
              backgroundColor: user.role === "Admin" ? "#28a745" : "#17a2b8",
              color: "white",
              padding: "2px 8px",
              borderRadius: "12px",
              fontSize: "12px"
            }}
          >
            {user.role}
          </span>
        </div>
      ))}
    </div>
  );
}

Nested Lists#

function CategoryList() {
  const categories = [
    {
      id: 1,
      name: "Fruits",
      items: ["Apple", "Banana", "Orange"]
    },
    {
      id: 2,
      name: "Vegetables", 
      items: ["Carrot", "Broccoli", "Spinach"]
    }
  ];
  
  return (
    <div>
      {categories.map(category => (
        <div key={category.id} style={{ marginBottom: "20px" }}>
          <h3>{category.name}</h3>
          <ul>
            {category.items.map((item, index) => (
              <li key={`\${category.id}-\${index}`}>
                {item}
              </li>
            ))}
          </ul>
        </div>
      ))}
    </div>
  );
}

Advanced List Operations#

Filtering Lists#

function FilterableList() {
  const [filter, setFilter] = useState("");
  const [items] = useState([
    "Apple", "Banana", "Cherry", "Date", "Elderberry"
  ]);
  
  const filteredItems = items.filter(item =>
    item.toLowerCase().includes(filter.toLowerCase())
  );
  
  return (
    <div>
      <input
        type="text"
        placeholder="Filter items..."
        value={filter}
        onChange={(e) => setFilter(e.target.value)}
        style={{ marginBottom: "10px", padding: "5px" }}
      />
      
      <ul>
        {filteredItems.map(item => (
          <li key={item}>{item}</li>
        ))}
      </ul>
      
      {filteredItems.length === 0 && (
        <p>No items match "{filter}"</p>
      )}
    </div>
  );
}

Sorting Lists#

function SortableList() {
  const [sortBy, setSortBy] = useState("name");
  const [sortOrder, setSortOrder] = useState("asc");
  
  const [people] = useState([
    { id: 1, name: "Alice", age: 30 },
    { id: 2, name: "Bob", age: 25 },
    { id: 3, name: "Charlie", age: 35 }
  ]);
  
  const sortedPeople = [...people].sort((a, b) => {
    let comparison = 0;
    
    if (sortBy === "name") {
      comparison = a.name.localeCompare(b.name);
    } else if (sortBy === "age") {
      comparison = a.age - b.age;
    }
    
    return sortOrder === "asc" ? comparison : -comparison;
  });
  
  return (
    <div>
      <div style={{ marginBottom: "10px" }}>
        <label>
          Sort by: 
          <select 
            value={sortBy} 
            onChange={(e) => setSortBy(e.target.value)}
            style={{ margin: "0 10px" }}
          >
            <option value="name">Name</option>
            <option value="age">Age</option>
          </select>
        </label>
        
        <label>
          Order: 
          <select 
            value={sortOrder} 
            onChange={(e) => setSortOrder(e.target.value)}
            style={{ margin: "0 10px" }}
          >
            <option value="asc">Ascending</option>
            <option value="desc">Descending</option>
          </select>
        </label>
      </div>
      
      <ul>
        {sortedPeople.map(person => (
          <li key={person.id}>
            {person.name} (Age: {person.age})
          </li>
        ))}
      </ul>
    </div>
  );
}

Practice Exercise: Build a Contact List#

Try building this contact list with the following features:

Requirements:

  1. Display a list of contacts with name, email, and phone
  2. Add new contacts with a form
  3. Delete contacts
  4. Search/filter contacts by name
  5. Show total count

Starter code:

function ContactList() {
  const [contacts, setContacts] = useState([
    { id: 1, name: "John Doe", email: "john@example.com", phone: "555-0101" },
    { id: 2, name: "Jane Smith", email: "jane@example.com", phone: "555-0102" }
  ]);
  
  const [searchTerm, setSearchTerm] = useState("");
  const [newContact, setNewContact] = useState({
    name: "",
    email: "",
    phone: ""
  });
  
  // Your code here!
  // Implement: addContact, deleteContact, filteredContacts
  
  return (
    <div style={{ padding: "20px", maxWidth: "600px" }}>
      <h2>๐Ÿ“ž Contact List</h2>
      
      {/* Search */}
      <input
        type="text"
        placeholder="Search contacts..."
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        style={{ width: "100%", padding: "10px", marginBottom: "20px" }}
      />
      
      {/* Add Contact Form */}
      <div style={{ marginBottom: "20px", padding: "15px", border: "1px solid #ddd" }}>
        <h3>Add New Contact</h3>
        {/* Add form inputs here */}
      </div>
      
      {/* Contact List */}
      <div>
        <h3>Contacts ({/* show count */})</h3>
        {/* Display filtered contacts here */}
      </div>
    </div>
  );
}

Common List Mistakes and Solutions#

Mistake 1: Missing Keys#

// โŒ React will warn about missing keys
{items.map(item => <li>{item}</li>)}

// โœ… Always include keys
{items.map(item => <li key={item.id}>{item}</li>)}

Mistake 2: Mutating State Arrays#

// โŒ Don't mutate the original array
const addItem = (newItem) => {
  items.push(newItem); // Bad!
  setItems(items);
};

// โœ… Create a new array
const addItem = (newItem) => {
  setItems([...items, newItem]); // Good!
};

Mistake 3: Using Index as Key for Dynamic Lists#

// โŒ Problematic when list items can be reordered
{items.map((item, index) => (
  <div key={index}>{item}</div>
))}

// โœ… Use unique, stable identifiers
{items.map(item => (
  <div key={item.id}>{item}</div>
))}

Mistake 4: Not Handling Empty Lists#

// โŒ Shows nothing when list is empty
<ul>
  {items.map(item => <li key={item.id}>{item.name}</li>)}
</ul>

// โœ… Provide feedback for empty states
{items.length === 0 ? (
  <p>No items found</p>
) : (
  <ul>
    {items.map(item => <li key={item.id}>{item.name}</li>)}
  </ul>
)}

Performance Tips for Lists#

Tip 1: Use Unique, Stable Keys#

  • Helps React optimize re-renders
  • Prevents component state loss
  • Improves animation performance

Tip 2: Keep List Items Simple#

  • Extract complex logic to separate functions
  • Avoid creating objects/functions in render
  • Use React.memo for expensive list items

Tip 3: Handle Large Lists Carefully#

  • Consider virtualization for 1000+ items
  • Implement pagination when appropriate
  • Use search/filtering to reduce displayed items

What We've Learned#

Congratulations! You now know how to:

โœ… Render arrays of data with map()
โœ… Use keys properly for optimal performance
โœ… Build interactive lists with add/remove functionality
โœ… Filter and sort lists dynamically
โœ… Handle empty states and edge cases
โœ… Avoid common list rendering pitfalls

Quick Recap Quiz#

Test your knowledge:

  1. What JavaScript method do you use to render arrays in React?
  2. Why are keys important in React lists?
  3. When is it okay to use array index as a key?
  4. How do you add an item to a state array without mutating it?

Answers: 1) map(), 2) They help React track and optimize list updates, 3) When the list order never changes, 4) Use spread operator: [...items, newItem]

What's Next?#

In our next lesson, we'll learn about useEffect - React's powerful hook for handling side effects like API calls, timers, and cleanup. You'll discover how to fetch data for your lists, respond to changes, and manage component lifecycle.

With useEffect, your lists can become truly dynamic by loading data from APIs and updating in real-time!

Previous

Event Handling

Next

React State