Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 29

Dynamic Lists and Conditional Rendering

Recommended resources

Hello! In our last lesson, we mastered useEffect and its dependency array, which is crucial for fetching data when a component mounts or when its props change. Now that we know how to get data into our components, the next logical step is to learn how to display it effectively.

Today, we will cover two fundamental UI patterns in React: rendering dynamic lists and applying conditional rendering. You'll learn how to take an array of data, such as a list of users or products fetched from an API, and turn it into a list of components on the screen. We'll also explore different ways to show, hide, or alter content based on application state, such as displaying a loading message or filtering a list based on a certain criteria.

These skills are the building blocks for almost any dynamic interface you'll create as a web developer, from social media feeds to e-commerce catalogs.

Part 1: Rendering Dynamic Lists

In modern web applications, you rarely hardcode lists of content. Instead, you work with dynamic data, typically in the form of a JavaScript array of objects. Your task is to transform that data into a list of UI elements.

The primary tool for this job in JavaScript is the Array.prototype.map() method, which you've used before. In React, we use it to transform an array of data into an array of JSX elements.

Let's see the basic pattern. Imagine you have an array of strings:

const fruits = ['Apple', 'Orange', 'Banana'];

To render this as a list in React, you would map over the array inside your JSX, returning a <li> for each item:

function FruitList() {
  const fruits = ['Apple', 'Orange', 'Banana'];

  const listItems = fruits.map(fruit => <li>{fruit}</li>);

  return <ul>{listItems}</ul>;
}

Notice how we embed the listItems array directly into the <ul>. React is smart enough to render each element in the array sequentially.

How to render LISTS in React 📃

This video from Bro Code provides a clear, practical demonstration of this exact process. It starts with a simple array of strings and shows how to use .map() to convert it into a list of elements.

Watch from the beginning until 03:11. Pay close attention to how the fruits array is transformed into an array of <li> elements using the .map() method.

The All-Important key Prop

If you run the code from the video, you'll notice a warning in your browser's console: Warning: Each child in a list should have a unique "key" prop.

This is one of the most important rules in React. When you render a list, you must provide a special prop called key to each element in the array.

Why is key necessary?
React uses the key to identify which items have changed, been added, or been removed. Think of it like a primary key in a database table. It gives each element a stable identity. Without keys, React has to work much harder to figure out what changed, which can lead to performance problems and bugs, especially with dynamic lists where items can be reordered, added, or deleted.

Rendering Lists

The official React documentation gives the best explanation for why keys are so critical. Reading this will give you a solid mental model.

Please read the section titled 'Keeping list items in order with key'. Focus on the 'Why does React need keys?' part. The analogy of file names on a desktop is very helpful.

Where do you get keys?

  • From your data: The best key is a unique string or number that already exists in your data, like a user ID, a product SKU, or a database primary key.
  • Locally generated: If your data doesn't have a unique ID, you can add one when you create it (e.g., using crypto.randomUUID()).

A common mistake is to use the array index as a key (data.map((item, index) => <li key={index}>...</li>)). Avoid this. If the array is re-ordered, the indices change, which defeats the purpose of the key and can lead to bugs.

Now let's see how to correctly render a list of objects with proper keys.

How to render LISTS in React 📃

The same Bro Code video continues by refactoring the example to use an array of objects, each with a unique id. This is the standard pattern you'll use in real applications.

Watch from 03:22 to 07:09. Observe how the key prop is set to fruit.id and how other properties like fruit.name and fruit.calories are used to render the content of each list item.

Test your understanding!

You have an array of user objects. Your task is to render an unordered list (<ul>) where each list item (<li>) displays the user's name and email.

Data:

const users = [
  { id: 'a4fde', name: 'Alice', email: 'alice@example.com' },
  { id: 'b8d9c', name: 'Bob', email: 'bob@example.com' },
  { id: 'c2a1e', name: 'Charlie', email: 'charlie@example.com' }
];

Write a React component UserList that takes this users array and correctly renders the list. Remember to include the key prop!

Show answer

Here is one possible solution. The core logic is mapping over the users array, and for each user object, returning a <li> element. The key is set to the unique user.id, and the content is constructed from user.name and user.email.

function UserList() {
  const users = [
    { id: 'a4fde', name: 'Alice', email: 'alice@example.com' },
    { id: 'b8d9c', name: 'Bob', email: 'bob@example.com' },
    { id: 'c2a1e', name: 'Charlie', email: 'charlie@example.com' }
  ];

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>
          <strong>{user.name}</strong> ({user.email})
        </li>
      ))}
    </ul>
  );
}

Part 2: Conditional Rendering

Your components won't always render the same thing. They need to adapt to changing data and state. For example:

  • Show a "Loading..." message while fetching data.
  • Display an error if an API call fails.
  • Render a list of "In Stock" items from a larger product list.
  • Show a user's profile page if they are logged in, or a login form if they are not.

React doesn't have special syntax for this; you just use standard JavaScript. Let's explore the most common patterns.

Pattern 1: Filtering Lists Before Rendering

A very common form of conditional rendering is to display a subset of a list. You can achieve this by chaining Array.prototype.filter() before you map(). This is a clean, declarative approach.

data.filter(condition).map(item => <Component.../>)

Rendering a List of Components in React

This video by Sam Meech-Ward demonstrates the .filter().map() pattern beautifully. It's a powerful and readable way to conditionally render list items.

Watch from 07:13 to 08:06. The video shows how to chain .filter() before .map() to only render jokes with a rating greater than three.

Rendering Lists

The React documentation also has a great example of filtering a list of scientists to show only the chemists.

Read the short section titled 'Filtering arrays of items'. It reinforces the .filter().map() pattern we just saw.

Pattern 2: Ternary Operator (? :)

For simple "if-this-then-that" logic directly inside your JSX, the ternary operator is perfect.

{condition ? <A /> : <B />}

This is extremely useful for toggling between two states, like a loading spinner and the final content.

function DataFetcher() {
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(true);

  // useEffect to fetch data would go here...

  return (
    <div>
      {isLoading ? <p>Loading...</p> : <ul>{/* Render data here */}</ul>}
    </div>
  );
}

Pattern 3: Logical AND Operator (&&)

If you want to render something only when a condition is true (and render nothing otherwise), the logical AND && operator is a concise shortcut.

{condition && <A />}

This works because in JavaScript, true && expression always evaluates to expression, and false && expression always evaluates to false. React doesn't render false, null, or undefined, so the component simply disappears.

function Notifications({ messages }) {
  return (
    <div>
      {messages.length > 0 && <h2>You have {messages.length} unread messages.</h2>}
      {/* ... list of messages ... */}
    </div>
  );
}

Important Pitfall: Do not use a number as the condition. 0 && <Component /> will not render nothing; it will render the number 0 on your page! Always use a boolean expression like messages.length > 0.

Pattern 4: if/else Statements with Variables

When your logic is too complex for a ternary or &&, fall back to a standard if/else statement. You can declare a let variable and conditionally assign JSX to it before the return statement.

function LoginButton() {
  const isLoggedIn = false; // This would come from state or props

  let content;
  if (isLoggedIn) {
    content = <button>Log Out</button>;
  } else {
    content = <button>Log In</button>;
  }

  return <div>{content}</div>;
}

This approach is the most verbose but also the most flexible and often the easiest to read for complex scenarios.

Conditional Rendering

Let's review these conditional patterns in the official React documentation. This will solidify your understanding of when to use each one.

Read the sections 'Conditional (ternary) operator (? :)', 'Logical AND operator (&&)' (paying attention to the pitfall about numbers), and 'Conditionally assigning JSX to a variable'. This covers the core patterns we just discussed.


Conclusion

You've just learned two of the most essential skills for building user interfaces in React. Being able to dynamically generate UI from data and conditionally show or hide parts of that UI is fundamental to creating interactive applications.

Key Takeaways:

  • Use Array.prototype.map() to transform an array of data into an array of JSX elements.
  • Always provide a unique and stable key prop for each element in a list to help React track items efficiently. The best key comes from your data (e.g., an id).
  • Use Array.prototype.filter() before map() to conditionally render a subset of a list.
  • For general conditional rendering in JSX:
    • Use the ternary operator (? :) for simple if/else choices.
    • Use the logical AND operator (&&) to render something only if a condition is true.
    • Use standard if/else statements with a variable for more complex logic.

In our next lesson, we'll focus on creating controlled components to manage and validate form inputs. You'll see how today's skills are directly applicable, as you'll be conditionally rendering validation errors and updating lists of results based on what the user types.