home / skills / flpbalada / my-opencode-config / react-key-prop

react-key-prop skill

/skills/react-key-prop

This skill helps you apply correct key prop usage in React lists by promoting stable, unique IDs and highlighting when to avoid index keys.

npx playbooks add skill flpbalada/my-opencode-config --skill react-key-prop

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
2.8 KB
---
name: react-key-prop
description: Guides proper usage of the key prop in React lists. Use this skill when rendering lists, mapping arrays to components, or troubleshooting list-related state bugs.
---

# React: Key Prop Best Practices

## Core Principle

**Use stable, unique IDs from your data. Never use array index for dynamic lists.**

The `key` prop provides stable identity to list elements during React's reconciliation process.

## When to Use What

### Use Data IDs (Preferred)

Always use unique, stable identifiers directly from your data:

```jsx
// ✅ Correct
{todos.map((todo) => (
  <li key={todo.id}>{todo.text}</li>
))}
```

Ideal keys are:
- **Unique** - No two items share the same key
- **Stable** - Never changes during component lifetime
- **Predictable** - Directly tied to the data item

### Generate IDs on Data Load

When data lacks IDs, create them **once** when receiving data:

```jsx
import { nanoid } from 'nanoid';

useEffect(() => {
  fetch('/api/items')
    .then(res => res.json())
    .then(data => {
      const itemsWithIds = data.map(item => ({
        ...item,
        _tempId: nanoid() // Stable ID generated once
      }));
      setItems(itemsWithIds);
    });
}, []);
```

### When Index Is Acceptable (Rare)

Index as key is acceptable ONLY when ALL conditions are met:
- List is absolutely static
- Items never added/removed (except at the end)
- Order never changes
- Items have no internal state

## Anti-Patterns to Avoid

### Never Generate Keys During Render

```jsx
// ❌ WRONG: Creates new key every render
{items.map(item => (
  <li key={Math.random()}>{item.name}</li>
))}
```

This forces React to destroy and recreate all components on every render.

### Don't Use Index for Dynamic Lists

```jsx
// ❌ WRONG for dynamic lists
{items.map((item, index) => (
  <li key={index}>{item.name}</li>
))}
```

Index fails when:
- Items are added/removed from beginning or middle
- List order changes (sorting, filtering)
- Items have internal state (like form inputs)

**The bug:** Index represents position, not data identity. When positions change but indexes stay the same, React incorrectly "mutates" existing components instead of creating new ones, causing state mismatch.

### Don't Use `useId()` for List Keys

React's `useId()` hook is for accessibility (linking labels to inputs), not for generating list keys.

## Quick Reference

### DO
- Always use `key` when rendering lists
- Prefer unique, stable `id` from your data
- Generate IDs once at data load time (`nanoid`/`uuid`)

### DON'T
- Never generate `key` during render (`Math.random()`, `Date.now()`)
- Avoid `index` as `key` for dynamic lists
- Don't use `useId()` for list keys

## References

- [React Docs - Rendering Lists](https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key)
- [nanoid - Tiny ID generator](https://github.com/ai-cookie/nanoid)

Overview

This skill guides correct usage of React's key prop when rendering lists and mapping arrays to components. It focuses on choosing stable, unique identifiers, avoiding common anti-patterns, and preventing list-related state bugs. Use it to enforce reliable reconciliation and predictable UI updates.

How this skill works

The skill inspects list rendering patterns and recommends appropriate keys based on data shape and mutation patterns. It highlights safe strategies (use data IDs, generate IDs once at load) and flags risky patterns (index keys for dynamic lists, keys generated every render). It explains the reconciliation consequences so developers can fix state mismatches and unnecessary re-renders.

When to use it

  • When rendering arrays with map to produce React elements
  • When mapping server-provided items that may lack IDs
  • When debugging disappearing, duplicated, or unexpectedly preserved component state in lists
  • When implementing sortable, filterable, or editable lists
  • When reviewing code for performance and reconciliation correctness

Best practices

  • Prefer unique, stable IDs from your data as key values
  • If data lacks IDs, generate IDs once when loading or normalizing data (e.g., nanoid/uuid) and store them with the items
  • Never create keys during render (avoid Math.random(), Date.now(), or new ID calls inside map)
  • Avoid using array index as key except for truly static lists that never change order or content
  • Do not use React's useId() for list keys; use it only for accessibility linking

Example use cases

  • Rendering a todo list using todo.id as key to preserve input focus and local state
  • Fetching items from an API, assigning _tempId with nanoid once, and using that for keys
  • Building a sortable list where stable keys prevent losing component state after reordering
  • Troubleshooting form inputs inside a mapped list that lose focus or reset—likely an unstable key issue
  • Code review checklist item: verify no keys are generated inside render and indexes are not used for dynamic lists

FAQ

Why is using array index as key bad?

Index ties identity to position; when items are inserted, removed, or reordered, indexes change and React may reuse the wrong component instance, causing state and UI mismatches.

What do I do if my data has no IDs?

Generate stable IDs once when you receive or normalize the data (store them on each item). Use libraries like nanoid or uuid so keys remain constant across renders.

Is useId() a valid key generator for lists?

No. useId() is for accessibility IDs and can collide or change across renders; it is not intended to provide stable list identity.