Skip to main content

React Key Prop: Guide to Efficient List Rendering

The key prop is a special identifier that tells React which items in a list have changed, been added, or removed. Using stable keys dramatically improves list rendering performance and prevents subtle bugs with component state, while relying on array indexes as keys is a common pitfall that leads to reconciliation errors.

Key Takeaways

  • Keys Identify List Items: Keys help React match list items across re-renders, enabling efficient DOM updates.
  • Stable IDs Are Required: Keys must be unique among siblings and never change—database IDs are ideal.
  • Never Use Array Index as Key: Index-based keys break when lists are reordered, filtered, or sorted.
  • Keys Are React-Internal: You cannot access this.props.key in a component; pass the ID as a separate prop if needed.
  • Changing a Key Resets State: Deliberately changing a key forces a component to unmount and remount, resetting its internal state.

Prerequisites

Before reading this article, ensure you understand:

  • Array Rendering: How to use .map() to render lists of components in JSX.
  • React Components: How components mount, update, and unmount in the component lifecycle.
  • Component State: Basic understanding of how React tracks component instances.

Why Does React Need Keys?

React's reconciliation algorithm (the process of updating the DOM) needs a way to identify which list items changed. Without keys, React assumes items are identified by their position—if a list is reordered, React treats it as if items were moved rather than understanding that specific items changed.

Think of it like a filing system: if you only labeled files by position ("first file," "second file"), removing the first file would make the second file become the "first file" in everyone's mind. Keys are like unique file names that stay with each item regardless of position.

// Without keys, React has to guess which items changed
const items = ['Alice', 'Bob', 'Charlie'];
{items.map((name) => <li>{name}</li>)} // ❌ Missing keys

// With keys, React knows exactly which item is which
{items.map((item) => <li key={item.id}>{item.name}</li>)} // ✅ Stable keys

When React lacks stable keys, it may:

  • Re-render more elements than necessary (performance hit).
  • Apply state changes to the wrong list items.
  • Lose internal state (form inputs, animations, focus).

How to Add Keys to Lists

The rule is straightforward: any element returned from .map() needs a key prop. The key should be a stable, unique identifier from your data.

import React from 'react';

const people = [
{ id: 1, name: 'Creola Katherine Johnson' },
{ id: 2, name: 'Mario José Molina-Pasquel Henríquez' },
{ id: 3, name: 'Mohammad Abdus Salam' },
];

function ScientistList() {
const listItems = people.map((person) =>
<li key={person.id}>{person.name}</li>
);
return <ul>{listItems}</ul>;
}

export default ScientistList;

Code Breakdown:

  • key={person.id}: The key prop is added to the top-level JSX element returned from map (the <li>).
  • Unique Among Siblings: Each key must be unique within the same parent list. Two lists on the same page can both use id: 1 without conflict.
  • Stable Across Renders: The person.id stays the same regardless of reordering or filtering.

Once keys are added, the console warning disappears and React can correctly track list items across re-renders.

What Makes a Good Key?

Keys must satisfy two requirements:

1. Uniqueness Among Siblings Each key must be unique within the same parent list. You can reuse the same key in different lists without conflict.

2. Stability Across Renders A key should represent the same logical item every time the component renders. Never generate keys dynamically:

// ❌ Bad: generates new key on every render
items.map((item) => <li key={Math.random()}>{item.name}</li>)

// ❌ Bad: index changes when list is filtered
items.map((item, index) => <li key={index}>{item.name}</li>)

// ✅ Good: database ID or UUID
items.map((item) => <li key={item.id}>{item.name}</li>)

// ✅ Good: generated once and stored
items.map((item) => <li key={item.uuid}>{item.name}</li>)

Best Sources for Keys:

  • Database Primary Keys: The ideal choice—IDs are unique and stable by design.
  • Generated UUIDs: Libraries like uuid or crypto.randomUUID() create stable client-side IDs.
  • Composite Keys: For items without natural IDs, combine multiple fields: ${item.category}-${item.date}.

Important Note: The key prop is reserved—you cannot access it inside the component (this.props.key is undefined). If the component needs the ID, pass it as a separate prop:

// Key is for React; userId is for the component
<TodoItem key={item.id} userId={item.id} title={item.title} />

The Anti-Pattern: Using Array Index as Key

Using an item's position (array index) as the key is the most common mistake. It appears to work in static lists, but breaks the moment the list is reordered, filtered, or sorted.

// ❌ Anti-pattern: index-based keys
people.map((person, index) => (
<li key={index}>{person.name}</li>
))

Why Index Keys Fail:

When a list is reordered, the index values don't change—position 0 always exists. But now a different item occupies that position. React thinks the item changed, when actually its position did. This causes:

  • Reconciliation Errors: React updates the DOM incorrectly, applying changes to the wrong items.
  • State Bugs: If list items have internal state (checked checkboxes, input values), that state gets reassigned to different items after reordering.
  • Lost Focus/Animations: Form inputs lose focus because React unmounts and remounts the component.

Example of the Bug:

// List of tasks with checkboxes
const tasks = ['Buy milk', 'Walk dog', 'Code review'];

tasks.map((task, index) => (
<li key={index}>
<input type="checkbox" /> {task}
</li>
))

// User checks "Buy milk" (position 0)
// User reorders list: now "Walk dog" is at position 0
// Bug: The checkbox state moved to "Walk dog" because it's still key={0}!

Safe Exception: Index keys are acceptable only if the list is completely static—items are never added, removed, reordered, or filtered. For any dynamic list, use stable IDs.

Advanced: Resetting Component State by Changing Keys

The key prop has a powerful side effect: when a component's key changes, React treats it as a completely new instance. The old component is unmounted (destroying its state), and a new one is mounted.

This is useful for deliberately resetting a component's state without managing complex reset logic:

import React, { useState } from 'react';

function ProfilePage({ userId }) {
const [comment, setComment] = useState('');

return (
<div>
<h1>User {userId}</h1>
<textarea
value={comment}
onChange={(e) => setComment(e.target.value)}
placeholder="Add a comment"
/>
</div>
);
}

export default ProfilePage;
import React, { useState } from 'react';
import ProfilePage from './ProfilePage';

function App() {
const [userId, setUserId] = useState(1);

return (
<div>
<button onClick={() => setUserId(1)}>User 1</button>
<button onClick={() => setUserId(2)}>User 2</button>

{/* By using userId as the key, switching users resets ProfilePage's state */}
<ProfilePage key={userId} userId={userId} />
</div>
);
}

export default App;

When you click "User 2," the key changes from 1 to 2. React sees this as a new component and unmounts the old one, destroying the comment state. A fresh ProfilePage is mounted, so the textarea is empty—no manual reset logic needed.

This pattern is valuable for multi-step forms, user profile pages, or any scenario where you want a clean slate when the context changes.

Frequently Asked Questions

Can I Use a Combination of Fields as a Key?

Yes, if your data doesn't have a natural ID. Combine fields to create uniqueness: ${item.userId}-${item.date}. However, ensure the combination is truly unique and stable. This approach works but is less efficient than a single ID field.

What If My List Items Don't Have IDs?

Add IDs to your data structure. If items come from a database, use the primary key. For client-generated items, use crypto.randomUUID() or a library like uuid when creating items. Storing IDs is a best practice that benefits reconciliation performance.

Do Keys Affect Performance Significantly?

Keys directly affect reconciliation—React's algorithm for updating the DOM. Without keys, React re-renders more elements than necessary. With keys, React can precisely match items and move DOM nodes instead of recreating them. For large lists or frequent reordering, proper keys make a measurable performance difference.

Can I Access the Key Value Inside My Component?

No. The key prop is reserved for React's internal use. It doesn't appear in props. If your component needs the ID value, pass it as a separate prop: <Item key={item.id} id={item.id} />.

What About Lists Inside Fragments or Conditional Renders?

Keys are required on each element returned from .map(), regardless of parent structure. If you wrap list items in a Fragment, the key goes on the Fragment: <React.Fragment key={item.id}>...</React.Fragment>. Keys are not needed on conditional items—only on elements returned from .map().

Further Reading