React keys are easy until one data item renders more than one sibling. A common table pattern outputs a primary row and a detail row for each record. The shorthand fragment syntax looks tidy, but it cannot carry a key.
The broken version
items.map((item) => (
<>
<tr>
<td>{item.name}</td>
</tr>
<tr>
<td>{item.details}</td>
</tr>
</>
));The empty fragment is shorthand for a fragment with no props. You cannot attach key to it. Adding keys to the inner rows is not the same thing because React needs a key for the top-level item returned by the map.
The correct version
import { Fragment } from "react";
items.map((item) => (
<Fragment key={item.id}>
<tr>
<td>{item.name}</td>
</tr>
<tr>
<td>{item.details}</td>
</tr>
</Fragment>
));The explicit Fragment form accepts props, includingkey. React can now reconcile each two-row group as one logical item.
Where it bites
Watch for tables, definition lists, grouped cards, timeline entries, and any conditional branch where one item produces multiple adjacent elements. The warning may only appear after a data shape changes, which makes it feel unrelated to the rendering code.
A review rule
During review, scan every map(). If the callback returns a shorthand fragment, ask whether the top-level result needs a key. If yes, use explicit Fragment. If the callback returns a single element, key that element directly.
The fix is tiny, but it prevents noisy warnings and subtle row mismatches when data is inserted, removed, filtered, or sorted.