Conditional Rendering, Lists, and Stable Keys

React uses ordinary JavaScript to decide which elements appear and to turn collections of data into repeated UI. Stable keys help React track each item as the collection changes.

The three ideas at a glance

These ideas often work together: choose the data, transform it into elements, and give each repeated element a durable identity.

Choose

Render different JSX when conditions change.

Repeat

Use array methods to create one element per item.

Identify

Give siblings keys that remain tied to their data.

Conditional rendering

A component can return different JSX for different data. React does not add a special conditional syntax. Use JavaScript before the return, or use expressions inside JSX.

if and an early return

Use this when an entire branch of the component changes. It is often the clearest choice for loading, error, and empty states.

Conditional operator

Use condition ? a : b when exactly one of two values should appear in the same location.

Logical AND

Use condition && element when something should either appear or render nothing.

Early returns keep the main success path from becoming deeply nested.
function UserPanel({ user, isLoading, error }) {
  if (isLoading) {
    return <p>Loading profile…</p>;
  }

  if (error) {
    return <p>Could not load the profile.</p>;
  }

  return (
    <section>
      <h2>{user.name}</h2>
      {user.isAdmin ? <AdminTools /> : <MemberTools />}
      {user.hasAlerts && <AlertBadge />}
    </section>
  );
}

Watch for zero with &&. React renders numbers, so {items.length && <List />} renders 0 when the array is empty. Write {items.length > 0 && <List />} to make the condition a boolean.

Return null when a component should render nothing.
function Warning({ message }) {
  if (!message) {
    return null;
  }

  return <p role="alert">{message}</p>;
}

Rendering lists

Data usually arrives as an array. Use map() to transform each item into JSX. If only some items should appear, use filter() first or conditionally return null from the mapping callback.

This component filters completed tasks, then maps each task to one list item.
const tasks = [
  { id: 101, title: "Sketch the layout", done: true },
  { id: 102, title: "Build the header", done: false },
  { id: 103, title: "Check keyboard access", done: true },
];

function CompletedTasks() {
  const completed = tasks.filter(task => task.done);

  return (
    <ul>
      {completed.map(task => (
        <li key={task.id}>{task.title}</li>
      ))}
    </ul>
  );
}

An arrow callback that returns multi-line JSX needs parentheses after =>. If you add braces instead, write an explicit return.

Stable keys

A key is a string or number that identifies one element among its siblings. During the next render, React uses keys to match old items with new items, even if items move, arrive, or disappear.

  1. Unique among siblings.

    The same key may be reused in a different array, but two items in one rendered list must not share it.

  2. Stable across renders.

    Use an ID from the data. Do not create a key during rendering with Math.random() or a changing timestamp.

  3. Tied to the item.

    The key should describe the data object's identity, not its current position in the array.

Key choiceWhen it worksRisk
key={task.id}Best default for data with durable IDsLow
key={index}Only for a truly fixed list that never reorders, inserts, or deletesState can follow the wrong item after changes
key={Math.random()}Never for normal list renderingItems are recreated on every render
Put the key on the element returned directly by map(), not on a nested child.
function TaskList({ tasks }) {
  return (
    <ul>
      {tasks.map(task => (
        <TaskRow key={task.id} task={task} />
      ))}
    </ul>
  );
}

function TaskRow({ task }) {
  return <li>{task.title}</li>;
}

key is not a prop. React consumes it as identity metadata, so TaskRow cannot read props.key. Pass the value separately, such as <TaskRow key={task.id} taskId={task.id} />, when the child also needs it.

Check your understanding

  1. Which conditional form fits a loading state that replaces the whole component?
    Show answer

    An if with an early return is usually clearest. It handles the loading branch before the component reaches its main return.

  2. Why is an array index a fragile key when items can be reordered?
    Show answer

    The index describes a position, not an item. After a reorder, the same index may point to different data, so React can preserve local state or DOM state on the wrong row.

  3. Where should a key go when map() returns <TaskRow />?
    Show answer

    Place it on <TaskRow />, the element returned directly from the callback. A key inside the component is too deep to identify that sibling in the array.