# How to Provide a Unique React Key for Array Children: Best Practices and Performance Optimization

> Learn best practices for unique React keys in array children. Use stable IDs for O(N) reconciliation, preserve state, and optimize performance. Avoid performance issues.

- Repository: [Meta/react](https://github.com/facebook/react)
- Tags: best-practices
- Published: 2026-02-16

---

**Use a stable, intrinsic identifier (like `item.id`) as the `key` prop when rendering lists in React to enable O(N) reconciliation, preserve component state, and prevent unnecessary DOM operations.**

When rendering dynamic lists in the `facebook/react` repository, providing a **unique React key for array children** is essential for the reconciler to efficiently update the UI. Without stable keys, React cannot distinguish between moved, added, or removed items, leading to degraded performance and potential state bugs. This guide explains the internal mechanics of key validation and the exact best practices derived from the React source code.

## Why a Unique React Key Matters for Array Children

React reconciles UI updates by comparing the current component tree with the previous one. When a component renders an array of children, the **key** prop lets the reconciler quickly match each child to its previous instance using an internal map.

Providing a unique React key for array children enables three critical optimizations:

1. **Identify moved, added, or removed items** without re-creating every element.
2. **Preserve component state** (e.g., `useState` inside a list item) because the same component instance is reused.
3. **Avoid unnecessary DOM operations**, which boosts rendering performance, especially for large lists.

According to the React source code, missing or duplicate keys force the reconciler to fall back to a full diff, increasing work by **O(N²)** in worst-case scenarios. Stable keys restore the expected **O(N)** reconciliation cost.

## How React Validates Keys Internally

In development mode, React validates that each sibling has a unique "key" prop and throws a warning if it does not. The validation lives in the reconciler at:

```text
packages/react-reconciler/src/ReactChildFiber.js

```

The relevant code emits the warning:

```js
console.error(
  'Each child in a list should have a unique "key" prop.' +
  '%s%s See https://react.dev/link/warning-keys for more information.',
  currentComponentErrorInfo,
  childOwnerAppendix,
);

```

(source: [ReactChildFiber.js L221-L227](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactChildFiber.js#L221-L227))

This warning appears when React detects children with duplicate or missing keys during the reconciliation process.

## Best Practices for Unique React Keys in Arrays

### Use Stable Intrinsic Identifiers

Always prefer a value that uniquely identifies the data item, such as `item.id` from a database. This ensures the key remains consistent across renders regardless of array position.

```tsx
function TodoList({todos}: {todos: Array<{id: string; text: string}>}) {
  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.text}</li>   // ✅ stable unique key
      ))}
    </ul>
  );
}

```

### Avoid Using Array Index as a Key

Using `key={index}` is only acceptable when the list never changes order, never inserts or removes items, and the items have no internal state. In all other cases, the index causes state leakage and extra re-renders because React cannot distinguish between items when the array shifts.

```tsx
// ❌ Bad: using index as key when items can reorder
items.map((item, index) => <li key={index}>{item.name}</li>);

```

### Never Generate Random Keys on Render

Generating keys with `Math.random()` or `Date.now()` on every render defeats the purpose of keys. Because the key changes on every render, React treats each element as new, destroying and recreating DOM nodes unnecessarily.

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

```

### Prefer Primitive Key Values

React stringifies keys internally. Using a primitive (string or number) avoids extra object allocation and ensures predictable comparison behavior.

### Handling Lists Without Natural IDs

When you don't have a natural ID:

1. **Use the `useId` hook** (React 18+) to generate a stable, globally unique prefix, then combine it with an index that is stable for that render cycle.

```tsx
import {useId} from 'react';

function RandomItemList({items}: {items: string[]}) {
  const idPrefix = useId();               // stable across renders
  return (
    <ul>
      {items.map((item, i) => (
        <li key={`${idPrefix}-${i}`}>{item}</li> // ✅ deterministic key
      ))}
    </ul>
  );
}

```

2. **Transform data upstream** to include a deterministic ID before it reaches the component.

## Working with React.Children.toArray

The `React.Children.toArray` helper clones each child and automatically assigns a key if none is provided, using the child's original key or its position. This is useful when manipulating children (e.g., filtering, reversing) while preserving keys.

```tsx
function ReversedList({children}: {children: React.ReactNode}) {
  const normalized = React.Children.toArray(children);
  const reversed = normalized.slice().reverse(); // preserves keys
  return <>{reversed}</>;
}

/* Usage */
<ReversedList>
  <Item key="a" />
  <Item key="b" />
  <Item key="c" />
</ReversedList>

```

(source: [ReactChildren-test.js L830-L836](https://github.com/facebook/react/blob/main/packages/react/src/__tests__/ReactChildren-test.js#L830-L836))

## Common Pitfalls to Avoid

When providing a unique React key for array children, avoid these anti-patterns:

```tsx
// ❌ Bad: using index as key when items can reorder
items.map((item, index) => <li key={index}>{item.name}</li>);

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

```

These patterns force React to destroy and recreate components, losing internal state and causing significant performance degradation.

## Summary

- **Always provide a unique React key for array children** using stable, intrinsic identifiers like database IDs.
- **Never use array indices as keys** unless the list is static and stateless.
- **Avoid random or generated keys** on every render to prevent unnecessary component unmounting.
- **Prefer primitive values** (strings or numbers) for optimal reconciliation performance.
- **Use `React.Children.toArray`** when transforming child collections to preserve existing keys.
- **Reference [`packages/react-reconciler/src/ReactChildFiber.js`](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactChildFiber.js)** for the internal validation logic that warns about missing keys.

## Frequently Asked Questions

### Can I use the array index as a key in React?

You should only use the array index as a key when the list is completely static—meaning items never reorder, are never inserted or removed, and contain no internal state. In dynamic lists, using `key={index}` causes React to confuse component identities when the array shifts, leading to state bugs and unnecessary re-renders.

### What happens if I don't provide a key prop in React lists?

If you omit the key prop, React defaults to using the array index as the key. In development mode, React checks for missing keys in [`packages/react-reconciler/src/ReactChildFiber.js`](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactChildFiber.js) and emits a console error: "Each child in a list should have a unique 'key' prop." This warning indicates that reconciliation may be inefficient and state may not persist correctly across renders.

### Is it safe to use Math.random() for React keys?

No, generating keys with `Math.random()` or `Date.now()` is unsafe and counterproductive. Because these values change on every render, React treats every list item as a new component, destroying and recreating DOM nodes and losing all internal state. This forces the reconciler into an O(N²) diff algorithm, severely degrading performance.

### How does React use keys during reconciliation?

During the reconciliation phase, React builds a map of existing children keyed by their `key` value. When processing updates, React uses these keys to match current children with their previous instances in O(1) time. This allows React to identify which items moved, were added, or were removed, enabling the O(N) reconciliation algorithm. Without stable keys, React cannot perform this efficient mapping and must resort to slower comparison strategies.