Ticker

6/recent/ticker-posts

📋 React Lists & Keys – Stop Getting Weird UI Bugs When Rendering Lists

React Lists & Keys

😵 The Problem

Let's say you're building a product page.

You have some products:

const products = [
  { id: 1, name: "Laptop" },
  { id: 2, name: "Keyboard" },
  { id: 3, name: "Mouse" }
];

And you want to show them on the screen.

The first instinct is pretty obvious:

function ProductList() {
  return (
    <div>
      <p>Laptop</p>
      <p>Keyboard</p>
      <p>Mouse</p>
    </div>
  );
}

But obviously, you're not going to hard-code every product.

The data is dynamic.

So you use JavaScript's map():

function ProductList() {
  return (
    <div>
      {products.map(product => (
        <p>{product.name}</p>
      ))}
    </div>
  );
}

Now the list renders.

But React starts complaining:

Each child in a list should have a unique "key" prop.

You might think:

"It's rendering perfectly. Why does React care about this key?"

That's where React Lists and Keys become important.

💡 The Solution: Render Lists With map()

In React, JavaScript's map() is the most common way to turn an array of data into UI.

For example:

function ProductList() {
  const products = [
    { id: 1, name: "Laptop" },
    { id: 2, name: "Keyboard" },
    { id: 3, name: "Mouse" }
  ];

  return (
    <ul>
      {products.map(product => (
        <li key={product.id}>
          {product.name}
        </li>
      ))}
    </ul>
  );
}

The important part is:

key={product.id}

Now React has a way to identify each item.

Think of it like giving every item its own ID card.

Laptop    → key 1
Keyboard  → key 2
Mouse     → key 3

When the list changes, React can use those keys to understand what actually changed.

🧠 Why Does React Need Keys?

Imagine your list is:

Laptop
Keyboard
Mouse

Then you add:

Monitor

React needs to figure out:

"What changed between the previous list and the new list?"

Keys give React a stable identity for each item.

1 → Laptop
2 → Keyboard
3 → Mouse
4 → Monitor

Now React can understand that the first three items are still the same and a new item was added.

Without meaningful keys, React has less information about the identity of each item.

This becomes especially important when items are:

  • added
  • removed
  • reordered
  • edited

🔥 Keys Should Be Stable

This is important.

A good key comes from the data itself.

For example:

key={product.id}

Perfect.

Because the product ID stays associated with that product.

You don't want the key to change just because the item moved.

Think:

Product A → key 101
Product B → key 102
Product C → key 103

Even if the order changes:

Product C → key 103
Product A → key 101
Product B → key 102

The identity stays the same.

That's exactly what React needs.

⚠️ Don't Use Array Index Without Thinking

You'll often see this:

products.map((product, index) => (
  <li key={index}>
    {product.name}
  </li>
));

React won't complain.

And for some static lists, it may appear to work perfectly.

But there is a catch.

Imagine:

A
B
C

Keys:

0 → A
1 → B
2 → C

Now remove A.

The list becomes:

B
C

Keys are now:

0 → B
1 → C

React sees:

key 0 still exists
key 1 still exists

But the item behind those keys changed.

That's where subtle UI bugs can appear.

🧠 Why Index Keys Can Cause Real Bugs

The problem becomes more obvious with stateful list items.

Imagine a list of inputs:

function TodoList({ todos }) {
  return (
    <div>
      {todos.map((todo, index) => (
        <input
          key={index}
          defaultValue={todo.title}
        />
      ))}
    </div>
  );
}

Suppose the list is:

Buy milk
Learn React
Build project

Now remove:

Buy milk

The remaining list is:

Learn React
Build project

But the indexes changed.

React may reuse the existing DOM nodes based on those keys.

Now the UI can behave in ways you didn't expect.

This is why stable IDs are much safer for dynamic lists.

🎯 The Better Approach

Use a unique ID:

function TodoList({ todos }) {
  return (
    <div>
      {todos.map(todo => (
        <input
          key={todo.id}
          defaultValue={todo.title}
        />
      ))}
    </div>
  );
}

Now each todo keeps its identity even if the list changes.

Todo 101 → Buy milk
Todo 102 → Learn React
Todo 103 → Build project

Remove Todo 101?

No problem.

Todo 102 is still Todo 102.

🔥 Rendering Components From Lists

Lists become much more useful when each item is its own component.

Instead of:

function ProductList({ products }) {
  return (
    <div>
      {products.map(product => (
        <div key={product.id}>
          <h2>{product.name}</h2>
          <p>${product.price}</p>
        </div>
      ))}
    </div>
  );
}

You can extract the item:

function ProductCard({ product }) {
  return (
    <article>
      <h2>{product.name}</h2>
      <p>${product.price}</p>
    </article>
  );
}

Then:

function ProductList({ products }) {
  return (
    <div>
      {products.map(product => (
        <ProductCard
          key={product.id}
          product={product}
        />
      ))}
    </div>
  );
}

Notice something important.

The key stays on the element being created by map():

<ProductCard
  key={product.id}
  product={product}
/>

You normally don't need to pass that key as a normal prop.

🧩 key Is Not a Normal Prop

This catches people sometimes.

You might try:

function ProductCard(props) {
  console.log(props.key);
}

But key isn't available like a normal prop.

If the component needs the ID:

<ProductCard
  key={product.id}
  productId={product.id}
/>

Then:

function ProductCard({ productId }) {
  console.log(productId);
}

Keep these concepts separate:

key
↓
Used by React for element identity

productId
↓
Your application data

💡 Real Developer Insight

Here's the thing about keys:

Most developers only think about them because React shows a warning.

That's the wrong reason.

Keys aren't there just to make the warning disappear.

They're part of how React understands identity in a changing list.

When you're working with real applications, lists change constantly:

Add item
Remove item
Sort items
Filter items
Move items
Update item

If your keys represent stable identity, React has much better information about what happened.

If your keys are basically:

key={Math.random()}

you've done the exact opposite.

Every render creates new keys.

React now thinks:

"Cool. Everything is new."

That can cause unnecessary DOM work and can reset component state.

So don't think:

"What key will remove this warning?"

Think:

"What makes this item uniquely identifiable in my data?"

That's usually your key.

🚫 Never Use Random Values as Keys

This is a terrible idea:

key={Math.random()}

Why?

Because the value changes every render.

For example:

Render 1 → key 0.482
Render 2 → key 0.931
Render 3 → key 0.217

React sees different keys every time.

That means you're constantly destroying and recreating elements instead of preserving their identity.

If your data already has IDs, use them.

🧠 What If You Don't Have an ID?

Sometimes your data genuinely doesn't have an ID.

For a static list where items never reorder, insert, or delete, using the index can be acceptable:

const skills = [
  "JavaScript",
  "React",
  "TypeScript"
];

skills.map((skill, index) => (
  <li key={index}>
    {skill}
  </li>
));

The important part is understanding why it's safe.

If the list is dynamic:

Add
Remove
Reorder
Filter

you should look for a stable identifier instead.

For example, you might normalize the data when it enters your application rather than relying on array positions forever.

⚠️ Common Developer Mistakes

1. Forgetting the key

Wrong:

products.map(product => (
  <ProductCard product={product} />
));

Better:

products.map(product => (
  <ProductCard
    key={product.id}
    product={product}
  />
));

2. Using Index for Dynamic Lists

This:

key={index}

isn't automatically wrong.

But if the list can change order or have items inserted/removed, it's risky.

Prefer:

key={item.id}

when possible.

3. Using Random Keys

Never:

key={Math.random()}

Stable identity is the whole point of keys.

4. Trying to Read key From Props

This won't work:

function Item({ key }) {
  // ❌
}

Pass a separate prop if the component needs the value:

<Item
  key={item.id}
  itemId={item.id}
/>

5. Generating Keys From Values That Aren't Unique

This can also cause problems:

key={user.name}

What if two users are both named:

John

Now you have duplicate keys.

The key should be unique among the siblings and stable for the identity you're rendering.


🚀 Best Practice Summary

✅ Use map() to render dynamic lists from arrays
✅ Give every dynamic list item a stable and unique key
✅ Prefer database or application IDs over array indexes
✅ Avoid random values as keys because they change between renders
✅ Remember that key is for React identity, not a normal component prop

🎯 Conclusion

React Lists are pretty straightforward.

You have an array:

Data
 ↓
map()
 ↓
Components / Elements

The part that deserves more attention is the key.

Don't think of it as some annoying React requirement.

Think of it as identity.

React needs to know:

"Is this the same item I saw before, or is this a different one?"

A stable key gives React that answer.

And once you start building real applications with sortable tables, todo lists, shopping carts, filters, editable forms, and dynamic dashboards, you'll realize why getting keys right actually matters.

Don't choose a key just to remove the warning. Choose a key that represents the item's identity. 🚀

Reactions

Post a Comment

0 Comments