logo

React `key` practices

Understanding React key

Keys give the react elements a stable identity

Warning in array without key

import React from 'react'

function App() {
  const persons = ['mike', 'jason', 'sharky']
  return (
    <ul>
      {persons.map((p) => {
        return <li>{p}</li>
      })}
    </ul>
  )
}

React will throw a warning with the code above: Warning: Each child in a list should have a unique "key" prop.

What keys to use?

Keys should be unique and stable

Index ? Maybe, but only under some circumstances

Array index is unique across an array.

import React, { useState } from 'react'

function App() {
  const persons = ['mike', 'jason', 'sharky']
  return (
    <ul>
      {persons.map((p, index) => {
        return <li key={index}>{p}</li>
      })}
    </ul>
  )
}

However, if the order/number of items may change, array index is NOT stable.

  • mike
  • jason
  • sharky
import React, { useState } from 'react'

export default function App() {
  const [persons, setPersons] = useState(['mike', 'jason', 'sharky'])

  const onAdd = () => {
    setPersons(['fishman', ...persons])
  }
  return (
    <>
      <button
        onClick={onAdd}
        className="mb-4 rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700"
      >
        Add fishman on top
      </button>
      <ul className="space-y-2">
        {persons.map((p, index) => {
          return (
            <li key={index} className="flex items-center gap-2">
              <input type="checkbox" />
              {p}
            </li>
          )
        })}
      </ul>
    </>
  )
}

Try it yourself: Tick mike and then click the add button. You'll see the check status incorrectly stays with the first position instead of following mike.

Stable key

Usually we will have a unique and stable id like property for each record from backend/database.

  • mike
  • jason
  • sharky
import { useState } from 'react'

export default function App() {
  const [persons, setPersons] = useState([
    { id: 'uywoejk', name: 'mike' },
    { id: 'woeioqj', name: 'jason' },
    { id: 'eljlkqd', name: 'sharky' },
  ])

  const onAdd = () => {
    setPersons([
      {
        id: 'wuioeioe',
        name: 'fishman',
      },
      ...persons,
    ])
  }

  return (
    <>
      <button
        onClick={onAdd}
        className="mb-4 rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700"
      >
        Add fishman on top
      </button>
      <ul className="space-y-2">
        {persons.map((p) => {
          return (
            <li key={p.id} className="flex items-center gap-2">
              <input type="checkbox" />
              {p.name}
            </li>
          )
        })}
      </ul>
    </>
  )
}

Try it yourself: Now tick mike and click add. The checkbox correctly stays with mike because each person has a stable id as the key.

Common Key Anti-patterns

❌ Random values or timestamps

// DON'T DO THIS
{
  items.map((item) => <div key={Math.random()}>{item.name}</div>)
}

// OR THIS
{
  items.map((item) => <div key={Date.now()}>{item.name}</div>)
}

These generate new keys on every render, forcing React to unmount and remount all elements unnecessarily. This destroys state and kills performance.

❌ Duplicate keys

// BAD: Multiple items with same key
{
  items.map((item) => <div key={item.category}>{item.name}</div>)
}

Duplicate keys confuse React's reconciliation algorithm and can cause incorrect updates or lost state.

Advanced Key Concepts

Keys in fragments

When returning multiple elements from a component or mapping an array to fragments, you need to add keys to the fragments:

function Glossary({ items }) {
  return (
    <dl>
      {items.map((item) => (
        <React.Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.description}</dd>
        </React.Fragment>
      ))}
    </dl>
  )
}

Keys only need to be unique among siblings

Keys don't need to be globally unique—only unique among their siblings:

function Blog() {
  const posts = [{ id: 1, title: 'Post 1' }]
  const comments = [{ id: 1, text: 'Comment 1' }] // Same id is fine!

  return (
    <>
      {posts.map((post) => (
        <Post key={post.id} {...post} />
      ))}
      {comments.map((comment) => (
        <Comment key={comment.id} {...comment} />
      ))}
    </>
  )
}

Use key to unmount component

Use case: I have a select and input component. Each the select option changes, reset the input to default state.

Without key (state persists):
With key= (resets on change):
import { useState } from 'react'

export default function App() {
  const [option, setOption] = useState('')

  return (
    <div className="space-y-4">
      <div>
        <label htmlFor="key-unmount-demo-option-select" className="mr-2">
          Select option:
        </label>
        <select
          id="key-unmount-demo-option-select"
          onChange={(e) => setOption(e.target.value)}
          className="w-30 rounded border border-gray-300 px-2 py-1 dark:border-gray-600 dark:bg-gray-800"
        >
          <option value="">Choose...</option>
          <option value="a">A</option>
          <option value="b">B</option>
        </select>
      </div>

      <div>
        <div className="mb-1 text-sm text-gray-600 dark:text-gray-400">
          Without key (state persists):
        </div>
        <input
          defaultValue="this is default"
          className="rounded border border-gray-300 px-2 py-1 dark:border-gray-600 dark:bg-gray-800"
        />
      </div>

      <div>
        <div className="mb-1 text-sm text-gray-600 dark:text-gray-400">
          With key={option} (resets on change):
        </div>
        <input
          key={option}
          defaultValue="this is default"
          className="rounded border border-gray-300 px-2 py-1 dark:border-gray-600 dark:bg-gray-800"
        />
      </div>
    </div>
  )
}

Try it yourself: Type something in both inputs, then change the select. The second input (with key) resets because React unmounts and remounts it when the key changes.

Performance implications

Keys are critical for React's reconciliation performance. With proper keys, React can:

  • Efficiently reorder existing DOM nodes instead of recreating them
  • Preserve component state when items move in a list
  • Skip unnecessary re-renders of unchanged items

Without proper keys, React may needlessly destroy and recreate DOM nodes, losing state and degrading performance.

Conclusion

  • key is a unique identifier for react elements
  • key should be unique and stable. A react element with a different key in different render phases will be considered a different element. The old one will be unmounted and a new one is created.
  • Only use array indices as keys when the number/order of array items are unchanged across the whole app lifecycle.
  • Always prefer to use backend/database unique identifier id-like properties as key
  • Never use random values, timestamps, or non-stable data as keys
  • Keys only need to be unique among siblings, not globally
  • Proper keys are essential for optimal reconciliation performance