Blog / React

Everything you need to know about React Keys

Everything you need to know about React Keys

In this guide you will discover everything you need to know about React Keys to be able to use them properly in any React app along with the ins and outs.

Will MaygerWill Mayger
March 03, 2022
Article

The goal of this article is to serve as the ultimate guide to React keys so that no matter what you are looking to find out about them this guide will be able to help you with it.

React keys are an essential part of writing React code, and whilst you can usually get by without having too much understanding about what they are or what they do it is still important to understand so you don’t accidentally encounter edge cases and bugs in your code.

This post will not only explain what React keys are, but will also explain why they are needed, how to use them, React keys uniqueness, their relationship to lists and arrays, the index problem, and when they are/aren’t needed.

What are React keys?

React keys infographic

React Keys are a unique attribute that you need to provide to the root component or element as a “key” when looping over a list or array in React in order for React to be able to track and identify changes, additions or deletions in your array/list across renders.

Generally speaking your array will most likely contain some data array that you want to render into the UI as components or elements.

In most cases arrays like this will be used to create an array of new components directly within JSX using the Arrary.prototype.map method.

There are many other ways of looping but almost all will share the same principle of needing to use a key to track items within the array.

This brings us onto what the key actually is. Within your array of data you should have some kind of unique identifier for each item within your array amongst the various other properties.

This property can be anything as long as it won’t change and is unique to that array, so it could be an id, some text, a heading, a date and so on as long as it is unique within the array.

It is worth noting at this point that it only needs to be unique in that particular array, and does not have to be unique across all arrays in your application.

Here is a quick example that shows the choice of key and applying it to an element/component:

import React from "react";

const data = [
  {
    id: "abc",
    message: "Hello world",
    time: "9am",
  },
  {
    id: "xyz",
    message: "How is your day?",
    time: "9am",
  },
];

export default function App() {
  return (
    <>
      {data.map(item => (
        <p key={item.id}>{item.message}</p>
      ))}
    </>
  );
}

Using the same example here is another valid key choice:

import React from "react";

const data = [
  {
    id: "abc",
    message: "Hello world",
    time: "9am",
  },
  {
    id: "xyz",
    message: "How is your day?",
    time: "9am",
  },
];

export default function App() {
  return (
    <>
      {data.map(item => (
        <p key={item.message}>{item.message}</p>
      ))}
    </>
  );
}

Both of these choices are valid because the key is unique within the data array. Now let’s take a look at a bad key choice using the above example.

import React from "react";

const data = [
  {
    id: "abc",
    message: "Hello world",
    time: "9am",
  },
  {
    id: "xyz",
    message: "How is your day?",
    time: "9am",
  },
];

export default function App() {
  return (
    <>
      {data.map(item => (
        <p key={item.time}>{item.message}</p>
      ))}
    </>
  );
}

As you can see in this example we have used the time field as the key choice, the reason why this is a bad choice is because it is not unique within the array where other array items also have the same time value.

In the next section we are going to look into why the keys are actually needed and what problem they solve.

Why are React keys needed?

The reason as to why keys are needed in React goes fairly deep into “under the hood” of how React works, but to simplify it, keys are needed in React to track elements between renders so that React can update elements that need updating, and not update elements that don’t.

Here is an example of an issue that occurs due to not setting React keys:

Showing issue when no react keys are used

Each time a state or prop update occurs in React it triggers a render where it will create a new/different tree of React elements in comparison to the tree that can be seen in the UI.

You can think of trees of elements in React just like trees you find in nature, all trees have branches and all branches have more branches, in the case of React each branch is a React element and each sub branch is a child React element.

When this comparison happens, React needs to figure out the most efficient/performant way to compare and update the current UI tree with the newly generated tree from the render so that the user can see and interact with the latest state/prop changes.

React solves this problem by using a Reconciliation algorithm with a Big-O complexity of O(n) where n is the number of elements in a tree.

It would be too much for this article but if you want to read more into it there is a great article in the official React docs here.

But to over simplify it, it is just a better and more efficient way to update the UI in comparison to rebuilding each and every element on each render as long as we all follow the appropriate rules when it comes to keys.

Now you might be wondering, how do keys fit into rendering and updating the UI.

Well, in the process of matching and updating elements in trees React needs a way to see which elements need updating and which don’t.

So when you provide a key to elements within an array you are helping React to understand if something has changed or not in that array, as well as maintaining the correct order and only updating the UI where necessary.

Let’s take a look at an example to better understand and illustrate this issue.

import "./App.css";
import React, { useState } from "react";

export default function App() {
  const [list, setList] = useState(["Hello World"]);

  const pushToList = (item: any) => setList(l => [...l, item]);
  const prependToList = (item: any) => setList(l => [item, ...l]);

  return (
    <>
      <button onClick={() => prependToList("test start")}>Add to start</button>
      <button onClick={() => pushToList("test end")}>Add to end</button>
      <ul>
        {list.map(item => (
          <li>
            <span>{item}</span>
            <input />
          </li>
        ))}
      </ul>
    </>
  );
}

In the above code we have a list of strings that is controlled by the state within our component.

It is rendered into a list as list items along with an empty input, and as you might have seen we do not have a key set in this example.

The empty input here is going to be used to see how we can see the UI updating on each render.

If a user enters some text next to the first list item “Hello World” what would you expect to happen if we then added another item into the list?

You would expect that the input text from the user would remain with the appropriate list item, in this case “Hello World”.

Let’s take a look at what really happens:

Showing issue when no react keys are used from additions at the start

As you can see by adding a new item to the front of the list without a key set, React doesn’t have a way to know which list item has been updated, it only knows that it has changed and so the input text remains with the first index of the list and not with the correct list item that is now in the second position in the array.

When no key has been defined React will default the key to be the index of the array which works out fine as long as the ordering or size of the array does not change.

If it does then we experience an issue like in the above example where there has been an addition to the start of the list but React seems to move the input up into the new array entry rather than keeping it with the correct item.

As far as React is concerned the input is attached to the 0th element in the list, and when we add a new element to the front the new element becomes the 0th element which is why the input remains attached to the first list item rather than moving with the correct list item.

To imagine this a little better here is a rough example of how it would look in some JavaScript code:

const inputs = ["a"];

const inputKey = 0;

const getInputListItem = key => inputs[key];

console.log(getInputListItem(inputKey)); // 'a';

// unshift is to prepend to the front.
inputs.unshift("b");

console.log(getInputListItem(inputKey)); // 'b';

If however we add the item to the end it behaves as we would expect because the original list item and input remain at the 0th position.

Showing issue is not visible when no react keys are used from additions at the end

Lastly in this section let’s take a look at the above example but this time including a unique key for each item:

import "./App.css";
import React, { useState } from "react";

export default function App() {
  const [list, setList] = useState(["Hello World"]);

  const pushToList = (item: any) => setList(l => [...l, item]);
  const prependToList = (item: any) => setList(l => [item, ...l]);

  return (
    <>
      <button onClick={() => prependToList("test start")}>Add to start</button>
      <button onClick={() => pushToList("test end")}>Add to end</button>
      <ul>
        {list.map(item => (
          <li key={item}>
            <span>{item}</span>
            <input />
          </li>
        ))}
      </ul>
    </>
  );
}
Working example using React keys

The above example works as expected the whole time that each list item is unique within the list.

How to use React keys

As you might have seen in the above few examples you can add the key into the root element/component for a given list as an attribute.

The element can be any HTML element, or any React component including fragments unless it is the shorthand fragment, but you can read more about it here.

But the principle of how to use React keys is that a key is a unique property from your data that is unique within the array whilst avoiding using indexes as the key due to what we looked at in the previous section.

Here is a simplified example of how to use React keys:

const data = [
  { id: "abc", message: "Hello" },
  { id: "xyz", message: "World" },
];
const ExampleComponent = () => (
  <>
    {data.map(data => (
      <p key={data.id}>{data.message}</p>
    ))}
  </>
);

As you can see we convert an array of data into an array of components in our JSX with the use of data.map which loops over each item and returns something new.

In the new components we return from data.map, each gets the attribute key set with the id field from our data which is unique to each item in the array of data.

The React key index problem

As we covered this topic in depth in the above section “Why are React keys needed?” this will just go over it briefly in as few words as possible.

The Rect key index problem is a referencing issue based on where React will default to using indexes as the key if keys have not been explicitly set.

This is a problem whenever any kind of re-ordering, additions, or deletions occur within the array used because the new order or size will potentially change the keys of each item in the array to different indexes which can lead to strange edge cases where inputs and other elements might not have the data tracked correctly in the array.

When to use React keys

The simple answer to when to use React keys is all the time.

Any time you have to run over an array to generate components, you should always add in a unique key within that array.

It does not need to be globally unique, only unique within that array and should not be the index of the array unless the array will never change, be reordered, have additions or deletions or otherwise modified.

The only times when you don’t need to add a key is when dealing with normal elements and components that are not within a loop.

Summary

There we have everything you need to know about React Keys, if you want more like this be sure to check out some of my other posts!

Foxi - Budget Planner & Tracker

Foxi

Budget Planner & Tracker

More money in your pocket by the end of the month.

Free to use and no account needed.

Get started now.

Get the app

Some graphics used on this post were made using icons from flaticon.

Latest Posts

Learn React, JavaScript and TypeScript

Learn React, JavaScript and TypeScript

Join the platform that top tier companies are using.
Master the skills you need to succeed as a software engineer and take your career to the next level with Pluralsight.

Start here

Become an expert in ReactJS, TypeScript, and JavaScript.

Here you will find my personal recomendations to you, for full disclosure I earn a small commission from some of these links, but I only recommend what I trust and personally use.

Good things are coming, don't miss out!

Good things are coming, don't miss out!

Follow me on Twitter to stay up to date and learn frontend, React, JavaScript, and TypeScript tips and tricks!

Are you a novice, intermediate or expert react engineer?

Find out here by taking my fun, interactive, quick quiz which takes approximately 1 - 3 minutes. How well will you will do?

Foxi - Budget Planner & Tracker

Foxi

Budget Planner & Tracker

More money in your pocket by the end of the month.

Free to use and no account needed.

Get started now.

Get the app