Why React Keys Matter: An Introduction

Subscribe to my newsletter and never miss my upcoming articles

When working with any type of list in React, you will often encounter this warning if you forgot to include a key prop:

Warning: Each child in a list should have a unique “key” prop

So, why does React tell you to include keys and why is it important?

Hello everyone, in this article, I'll be explaining an underlying concept in React keys, why do they matter and how to use them correctly. Let's jump in!

What are React Keys?

Simply put, they are props that are passed in child elements of list in order to:

  • identify which elements are added
  • identify which elements are updated
  • identify which elements are removed

Hence, keys serve as identification for an element just like how passports are used to identify people.

Why do we need it?

At this point, you may wonder why do we need it? After all, we could identify elements by their id, className, parent/child, index, props, etc. The answer is because of React's Diffing Algorithm.

Diffing Algorithm: A Brief Explanation

A React app is made up of a tree of components. Whenever there's a prop or state change in any component, React re-renders its components into its virtual DOM. The diffing algorithm compares the new virtual DOM with the old DOM at each level of the component tree, starting from the root node.

0_w-WbewkV9FjwxvKu.png

The algorithm finds the minimum number of operations required to update the real DOM. This is how it does it:

1. Compare node by types (i.e. <div> vs <span>).

If different, destroy and build a new component from scratch.

// virtual DOM
<div><MyComponent/></div>

// real DOM
<span><MyComponent/></span>

E.g. This results in <MyComponent/> being destroyed and re-built.

2. If nodes have same type, compare by attributes.

If different, only update the attributes.

// virtual DOM
<div className="after" title="stuff" />

// real DOM
<div className="before" title="stuff" />

E.g. This results in an update of className to after.

What about lists?

For lists, React will recurse on both their children simultaneously, find any differences, then patch them to the real DOM if there are any.

// virtual DOM
<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

// real DOM
<ul>
  <li>first</li>
  <li>second</li>
</ul>

E.g. This results in the <li>third</li> being added after <li>second</li>.

So far so good? But now, instead of adding an element at the bottom of the list, what if we add a new element at the beginning?

// virtual DOM
<ul>
  <li>zero</li>
  <li>first</li>
  <li>second</li>
</ul>

// real DOM
<ul>
  <li>first</li>
  <li>second</li>
</ul>

This example will result in React re-rendering every single <li> to the real DOM because it doesn't realize that it can simply add <li>zero</li> to the beginning of the list.

This inefficiency can cause problems, especially in larger apps. Hence, keys provide a simple solution to this issue.

The Right Way to Use Keys: id

You can easily add keys to list elements like so:

<li key="1">first</li>
<li key="2">second</li>
<li key="3">third</li>

Keys should be a unique identifier so that each element can be identified properly. Hence, it is recommended to use some uniquely generated id as the key. You can even assign keys to a dynamically rendered list:

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);

The NOT Right Way to Use Keys: index

Using index as your key will result in issues when certain changes are made to the list. Below is a demo I made to illustrate this issue.

index.gif

Notice that when adding a new element to the student list, the Notes property is not mapped correctly because the list's index is the key.

As a result, the property's values (greenroots and mycodinghabits) will always be at index 0 and 1, even if their corresponding list elements has changed their position in the list.

Let's see what it looks like if we use Student ID as the key instead.

id.gif

Here's a side-by-side comparison:

react-keys.gif

Feel free to visit the demo or see the repo.

Conclusion

The main purpose of keys is to help React differentiate and distinguish elements from each other, increasing its performance when diffing between the virtual and real DOM. To use keys, simply add the prop inside an element such as <li>.

Unique ids is the best value to assign to keys. You can only use index as the key if the list is static (cannot be changed) and the elements in it have no id property.

Thank you for reading. I hope this article has been useful. Please like or share any comments below if you have questions. For further reading, check out the section below. Have a great day, cheers!


References

Bolaji Ayodeji's photo

Well explained Victoria Lo, thanks for sharing!

Victoria Lo's photo

Always a pleasure :)

LookRain's photo

Excellent writeup on the importance of key in React. However, are you sure the diffing is done on the virtual DOM and real DOM? To my understanding, the reconciliation happens between the most recent virtual DOM tree and the previous tree.

Victoria Lo's photo

Thank you for the correction. You are right, I meant to write that diffing is done between the old and virtual DOM in order to then update the real DOM. Sorry for the confusion.

Hardik Joshi's photo

So true! Recently I had a requirement to refresh a component, I initially thought of creating a state variable and refresh it based on that but adding a key solved the issue. I was looking for an article to explain "behind the scenes" and I landed here... Thank you so much for explaining! :)

Victoria Lo's photo

No problem! Glad it helped :)

Chris Short's photo

Great article!

Victoria Lo's photo

Thanks! Glad you like it :)

Linus Närkling's photo

Is the "key" attribute special or does React look for any attribute? I have a list of divs where the "title" attribute uniquely identifies the object - will that work just as well?

Dinys Monvoisin's photo

Interesting. I did not know about that. I never came across that.

So from what I understand, react does not re-render the input field when using index. That's why there is this issue. Am I right?

Victoria Lo's photo

In React, components will automatically re-render if there are state changes. In this case, the input field does not re-render because there is no change in its value.

And because its key is mapped to the index instead of id, the position of the input field does not change with its corresponding list item.