Skip to main content
ESLint Interlace
Plugin: react-featuresRules

jsx-key

jsx-key rule

Keywords: React, JSX, key prop, reconciliation, lists, map, iteration, ESLint rule, performance, LLM-optimized

Detect missing or problematic React keys that could break reconciliation. This rule is part of eslint-plugin-react-features and provides LLM-optimized error messages with suggestions.

Quick Summary

AspectDetails
SeverityError (correctness)
Auto-Fix💡 Suggests fixes
CategoryReact
ESLint MCP✅ Optimized for ESLint MCP integration
Best ForAll React/JSX projects

Rule Details

Why This Matters

IssueImpactSolution
🔄 ReconciliationReact can't track elementsAdd unique keys
PerformanceUnnecessary re-rendersStable keys
🐛 State BugsWrong component gets stateUse item IDs, not indexes
🎨 Animation IssuesElements animate incorrectlyConsistent key identity

Configuration

OptionTypeDefaultDescription
warnUnstableKeysbooleantrueWarn about potentially unstable keys

Examples

❌ Incorrect

// Missing key
function UserList({ users }) {
  return (
    <ul>
      {users.map((user) => (
        <li>{user.name}</li> // ❌ Missing key
      ))}
    </ul>
  );
}

// Using index as key (unstable)
function ItemList({ items }) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{item.name}</li> // ⚠️ Unstable key
      ))}
    </ul>
  );
}

✅ Correct

// Using unique ID as key
function UserList({ users }) {
  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>{user.name}</li> // ✅ Stable unique key
      ))}
    </ul>
  );
}

// Using compound key when no ID available
function ItemList({ items }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={`${item.category}-${item.name}`}>{item.name}</li> // ✅ Unique compound key
      ))}
    </ul>
  );
}

Configuration Examples

Basic Usage

{
  rules: {
    'react-features/jsx-key': 'error'
  }
}

Disable Unstable Key Warnings

{
  rules: {
    'react-features/jsx-key': ['error', {
      warnUnstableKeys: false
    }]
  }
}

Key Best Practices

Choosing Keys

SourceQualityExample
Database ID✅ Bestkey={user.id}
Unique field✅ Goodkey={item.slug}
Compound unique✅ Goodkey={item.cat + '-' + item.name}
UUID/nanoid⚠️ OKkey={generateId()}
Array index❌ Avoidkey={index}
Random number❌ Neverkey={Math.random()}

When Index Keys Are Acceptable

// ✅ Static list that never reorders/filters
const WEEKDAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'];
<ul>
  {WEEKDAYS.map((day, i) => (
    <li key={i}>{day}</li>
  ))}
</ul>;

// ❌ Dynamic list - DON'T use index
{
  users.map((user, i) => <UserCard key={i} user={user} />);
} // Bad!
{
  users.map((user) => <UserCard key={user.id} user={user} />);
} // Good!

Common Patterns

Fragment Keys

// When using fragments in lists, key goes on Fragment
function DataList({ data }) {
  return (
    <>
      {data.map((item) => (
        <React.Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.definition}</dd>
        </React.Fragment>
      ))}
    </>
  );
}

Nested Lists

function NestedList({ categories }) {
  return categories.map((category) => (
    <div key={category.id}>
      <h2>{category.name}</h2>
      <ul>
        {category.items.map((item) => (
          <li key={item.id}>{item.name}</li> // Separate key namespace
        ))}
      </ul>
    </div>
  ));
}

When Not To Use

ScenarioRecommendation
🧪 Testing/PrototypesConsider allowing index keys temporarily
📊 Static contentIndex keys may be acceptable
🔄 No reorderingIndex keys work for append-only lists

Comparison with Alternatives

Featurejsx-keyeslint-plugin-reactjsx-ally
Missing key✅ Yes✅ Yes❌ No
Unstable keys✅ Configurable⚠️ Limited❌ No
LLM-Optimized✅ Yes❌ No❌ No
ESLint MCP✅ Optimized❌ No❌ No
Suggestions✅ Yes⚠️ Limited❌ No

Further Reading

Known False Negatives

The following patterns are not detected due to static analysis limitations:

Dynamic Variable References

Why: Static analysis cannot trace values stored in variables or passed through function parameters.

// ❌ NOT DETECTED - Prop from variable
const propValue = computedValue;
<Component prop={propValue} /> // Computation not analyzed

Mitigation: Implement runtime validation and review code manually. Consider using TypeScript branded types for validated inputs.

Imported Values

Why: When values come from imports, the rule cannot analyze their origin or construction.

// ❌ NOT DETECTED - Value from import
import { getValue } from './helpers';
processValue(getValue()); // Cross-file not tracked

Mitigation: Ensure imported values follow the same constraints. Use TypeScript for type safety.