In React, does a key have to be unique across the whole page, and can the child component read the value you passed as its key?
answer
- compared only against siblings
- two lists, same numbers, no conflict
- looks like a prop, isn't one
- props.key is undefined
- pass the id twice on purpose
basics
~20 sA key only has to be unique among its siblings — the children of one parent in one array — so two separate lists may both use keys 1, 2, 3. The child cannot read it: React consumes key and never forwards it in props.
solid answer
~50 sKeys are scoped per sibling list, not globally. React only ever compares a child's key with the keys of the other children in the same array under the same parent, so two independent `<ul>`s on the page can both key their rows `1, 2, 3` with no conflict, and nested lists start their own namespace at each level. Globally unique ids are fine, just not required. The second half trips people up: `key` is a directive to React, not data for the component. It is stripped before props are assembled, so `props.key` is `undefined` inside the child — in React 19 reading it warns. If the child needs the identifier, pass it a second time as an ordinary prop, `<Row key={row.id} id={row.id} />`. And avoid spreading an object that contains a `key` into JSX; React 19 warns about that, so pass the key explicitly.
code
jsx · 16 linesexport function Groups({ groups }) {
return (
<ul>
{groups.map((group) => (
<li key={group.id}>
{group.name}
<ul>
{group.members.map((m) => (
<li key={m.id}>{m.name}</li>
))}
</ul>
</li>
))}
</ul>
);
}go deeper
Remember two sentences: keys need to be unique only among siblings, and the child cannot read its own key — pass the id again as a normal prop if it needs one.
Explain why: React only compares a child's key with the other children of the same parent, and key is lifted out of the element before props are built.
Handle the messy cases — duplicate ids inside one list, records with a missing id producing an undefined key, and objects spread into JSX that carry a key field.
Treat identity as a data contract: decide where ids come from, guarantee they are unique within the collections you render, and keep reconciliation identity separate from business identifiers even when they hold the same value.
## Keys are scoped to siblings When React reconciles children it compares the new children of *one parent* with the old children of *that same parent*. Keys are only ever compared inside that set. Concretely: ```jsx function Board({ todo, done }) { return ( <> <ul>{todo.map((t, i) => <li key={i}>{t.text}</li>)}</ul> <ul>{done.map((d, i) => <li key={i}>{d.text}</li>)}</ul> </> ); } ``` Both lists use `0, 1, 2`. That is not a collision, and React does not warn, because the two arrays live under different parents. (The index keys here are a separate problem — see list-mutation behaviour — but the *duplication across lists* is not one.) The same applies to nesting: a list of groups where each group renders its own list of members needs the group keys unique among groups, and the member keys unique only within their own group. A member id repeated across two groups is harmless. What *is* a real error is a duplicate inside one list. React logs `Encountered two children with the same key` and the pairing becomes ambiguous, so state can appear to leak between the two rows or one of them can be dropped. When your natural id is not unique per list — the same product appearing in two line items, say — build a composite key from fields that together are unique, or deduplicate upstream. Globally unique keys (UUIDs, database ids) are perfectly fine and usually what you have anyway. The point is that uniqueness beyond the sibling set buys you nothing, so "my ids collide across lists" is not a reason to invent prefixed keys. ## `key` is not a prop JSX attribute syntax makes `key` *look* like a prop, but React treats it specially. When an element is created, `key` (and, historically, `ref`) is pulled out and stored on the element itself, not in its props object. So inside the component: ```jsx function Row(props) { console.log(props.key); // undefined return <li>{props.label}</li>; } ``` `props.key` is `undefined`. React 19 additionally warns when you try to read `key` off a props object, precisely because people assume it is there. The fix is to pass the identifier twice when the child genuinely needs it: ```jsx {rows.map((row) => ( <Row key={row.id} id={row.id} label={row.label} /> ))} ``` That looks redundant and is not: one is React's identity for reconciliation, the other is data your component uses (to build a DOM `id`, to send a delete request, to look the record up). The mirror-image mistake is spreading a data object straight into JSX when that object happens to contain a `key` field: ```jsx <Row {...row} /> // row.key silently becomes the element's key ``` React 19 warns about a props object containing a `key` being spread into JSX, because the behaviour is surprising in both directions — a data field quietly becomes reconciliation identity, and it does not arrive as a prop. Pass the key explicitly and spread the rest. ## What a key may be A key is coerced to a string, so strings and numbers are the practical choices. Objects are not usable as keys — unlike a `Map`, React does not key by reference identity, and an object would stringify to something useless. Booleans, `null` and `undefined` are not valid identities either; `key={undefined}` behaves as if you had written no key at all, which is a common accident when the id field is missing from some records. Keys must also be stable: the same item must produce the same key on every render for the whole time it is in the list. ## Why this matters in interviews The two facts test the same understanding from opposite sides: a key is a *message to React about identity*, not a piece of the component's data and not a page-wide registry. Candidates who think keys are global invent prefixing schemes that add noise; candidates who think keys are props write components that read `undefined` and quietly break. Saying "scoped to siblings, consumed by React, pass the id separately if you need it" covers both in one breath.
- What if the same id legitimately appears twice inside one list?Then the id is not an identity for that list and you need a composite key — combine it with whatever distinguishes the two entries, such as `` `${item.productId}-${item.lineNo}` ``. Leaving the duplicate in place makes React warn and the child-to-child pairing ambiguous, which shows up as state appearing on the wrong row or one entry vanishing.
- Some records in the array have no id, so key={row.id} is undefined for them. What does React do?An `undefined` key is treated as no key at all: React falls back to index-based matching for those children and warns about the missing key. Mixing keyed and unkeyed siblings gives you the index-key failure modes on part of the list, which is harder to spot than having none. Fix the data or generate ids when it is loaded.
saying these in an interview costs you the question
- Keys must be unique across the whole application.
- You can read the key inside the child as props.key.
- Two lists with the same keys will collide.
- Spreading an object with a key field is harmless.
- Any value works as a key, including an object.