# react/jsx-key

> Require a `key` prop on elements in arrays and iterators

React uses `key` to tell list items apart from one render to the next. Without it, React warns at run time and matches items by position. After an insert or a reorder, an item can then end up with its neighbor's state, such as the text typed into an input.

The rule looks at elements returned from a `.map()` or `Array.from()` callback, and at elements in an array literal that React renders as a list: one that's a child of an element, or that a function returns. It skips lists passed to `React.Children.toArray()`, because that adds keys itself.

This rule is on by default if your `package.json` depends on `react`.

{/* Generated by src/codegen/lint-rule-docs.ts. Do not edit this section. */}

## Configure

On by default as an error. To turn it off:

```toml bunfig.toml
[lint.rules]
"react/jsx-key" = "off" # or "warn"
```

Every option, set to its default:

```toml bunfig.toml
[lint.rules]
"react/jsx-key" = ["error", {
  checkFragmentShorthand = false,
  checkKeyMustBeforeSpread = false,
  warnOnDuplicates = false,
  checkArraysOutsideJSX = false,
}]
```

{/* End of the generated section. */}

## Examples

Reported:

{/* bad {"count": 3} */}
```jsx
const list = (
  <ul>
    {users.map(user => <li>{user.name}</li>)}
  </ul>
);

const tabs = <nav>{[<Tab key="home" />, <Tab />]}</nav>;

const stars = Array.from({ length: 5 }, () => <Star />);
```

Not reported:

{/* good */}
```jsx
const list = (
  <ul>
    {users.map(user => <li key={user.id}>{user.name}</li>)}
  </ul>
);

const tabs = <nav>{[<Tab key="home" />, <Tab key="settings" />]}</nav>;

const panels = React.Children.toArray([<Panel />, <Panel />]);
```

An array that's only a place to keep elements doesn't need keys:

{/* good */}
```jsx
function Wizard({ step }) {
  const steps = [<Address />, <Payment />, <Review />];
  return steps[step];
}

const card = <Card actions={[<a href="#edit">Edit</a>, <a href="#more">More</a>]} />;
```

## Options

| Option | Default | What it does |
| --- | --- | --- |
| `checkFragmentShorthand` | `false` | Also report `<>...</>` in a list. It can't take a `key`, so use `<React.Fragment key={...}>`. |
| `checkKeyMustBeforeSpread` | `false` | Also report a `key` that comes after a `{...spread}`. |
| `warnOnDuplicates` | `false` | Also report siblings that have the same `key`. |
| `checkArraysOutsideJSX` | `false` | Also report elements in every other array literal. |

Only `bun lint` has `checkArraysOutsideJSX`. With an ESLint config file every array literal is checked, as in `eslint-plugin-react`.

{/* bad {"options": [{"checkArraysOutsideJSX": true}], "count": 2} */}
```jsx
const icons = [<Home />, <User />];
```

With `checkFragmentShorthand`:

{/* bad {"options": [{"checkFragmentShorthand": true}]} */}
```jsx
const rows = users.map(user => (
  <>
    <dt>{user.name}</dt>
    <dd>{user.email}</dd>
  </>
));
```

{/* good {"options": [{"checkFragmentShorthand": true}]} */}
```jsx
const rows = users.map(user => (
  <React.Fragment key={user.id}>
    <dt>{user.name}</dt>
    <dd>{user.email}</dd>
  </React.Fragment>
));
```

When a `key` comes after a spread, the automatic JSX runtime falls back to `createElement` for that element. `checkKeyMustBeforeSpread` reports it:

{/* bad {"options": [{"checkKeyMustBeforeSpread": true}]} */}
```jsx
const rows = users.map(user => <Row {...user} key={user.id} />);
```

{/* good {"options": [{"checkKeyMustBeforeSpread": true}]} */}
```jsx
const rows = users.map(user => <Row key={user.id} {...user} />);
```

With `warnOnDuplicates`:

{/* bad {"options": [{"warnOnDuplicates": true}]} */}
```jsx
const tabs = [<Tab key="home" />, <Tab key="home" />];
```
