[{"data":1,"prerenderedAt":128},["ShallowReactive",2],{"qa-\u002Freact\u002Fcomponents\u002Flists-keys":3},{"page":4,"siblings":108,"blog":125},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":20,"order":21,"path":22,"questions":23,"questionsCount":98,"related":99,"seo":100,"seoDescription":101,"stem":102,"subtopic":103,"topic":104,"topicSlug":105,"updated":106,"__hash__":107},"qa\u002Freact\u002Fcomponents\u002Flists-keys.md","Lists Keys",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"easy","md","React","react",{"subtopicSlug":19},"lists-keys",true,5,"\u002Freact\u002Fcomponents\u002Flists-keys",[24,28,32,37,41,45,49,53,57,61,65,69,73,77,81,85,89,93],{"id":25,"difficulty":14,"q":26,"a":27},"render-list","How do you render a list of items in React?","Use `Array.map` to transform a data array into an array of React elements.\nReact accepts an array of elements as a valid child.\n\n```jsx\nfunction FruitList({ fruits }) {\n  return (\n    \u003Cul>\n      {fruits.map(fruit => (\n        \u003Cli key={fruit.id}>{fruit.name}\u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  )\n}\n\n\u002F\u002F Usage\nconst fruits = [\n  { id: 1, name: 'Apple' },\n  { id: 2, name: 'Banana' },\n  { id: 3, name: 'Cherry' },\n]\n\u003CFruitList fruits={fruits} \u002F>\n```\n\nEach element in the mapped array must have a `key` prop so React can track\nit across renders.\n\n**Rule of thumb:** every `.map()` that produces JSX needs a `key` on the\noutermost returned element. If you forget it, React will warn in development\nand may produce incorrect diffs.\n",{"id":29,"difficulty":14,"q":30,"a":31},"what-is-key","What is the key prop and why is it required for lists?","`key` is a **special React prop** that gives each element in a list a\nstable identity. React uses it during reconciliation to match elements\nbetween renders and decide what to update, move, or unmount.\n\n```jsx\n{todos.map(todo => (\n  \u003CTodoItem key={todo.id} todo={todo} \u002F>\n  \u002F\u002F         ▲ unique, stable identifier\n))}\n```\n\nWithout keys, React falls back to positional matching: element 0 in the\nold list = element 0 in the new list. If you insert or delete items, React\nmay reuse the wrong component instance, leading to bugs like stale state,\nwrong animation, or incorrect DOM content.\n\nKeys must be:\n- **Unique among siblings** (not globally unique).\n- **Stable** — the same item should have the same key across renders.\n\n**Rule of thumb:** always key from your data's natural unique identifier\n(database id, UUID, slug). Never use array index as a default.\n",{"id":33,"difficulty":34,"q":35,"a":36},"index-as-key","medium","What problems arise when you use the array index as a key?","Index keys break when the list can be **reordered, filtered, or have items\ninserted\u002Fdeleted**.\n\n```jsx\n\u002F\u002F ❌ Index key — dangerous for mutable lists\n{items.map((item, i) => \u003CRow key={i} data={item} \u002F>)}\n\n\u002F\u002F ✅ Stable id key\n{items.map(item => \u003CRow key={item.id} data={item} \u002F>)}\n```\n\nThe problem: React identifies components by key. If you delete item at\nindex 0, everything shifts down by one — the component at index 1 now has\nkey `0`, so React thinks it's the same component as the old index-0 element.\nReact will update props but **not reset state**. This causes:\n- Controlled input fields retaining the wrong value.\n- Animations starting from the wrong position.\n- Incorrect checkboxes or selection states.\n\n**Rule of thumb:** use index as key only when: the list is read-only and\nnever reordered, items have no stable IDs, and the items have no internal\nstate (pure display).\n",{"id":38,"difficulty":34,"q":39,"a":40},"good-key","What makes a good key?","A good key is:\n1. **Unique among siblings** — two siblings can't share a key; siblings in\n   different lists can.\n2. **Stable** — the same data item has the same key across every render.\n3. **String or number** — React converts numbers to strings internally.\n\n```jsx\n\u002F\u002F ✅ Database primary key — best option\n\u003CItem key={item.id} \u002F>\n\n\u002F\u002F ✅ Slug\u002Fname if guaranteed unique per list\n\u003CTab key={tab.slug} \u002F>\n\n\u002F\u002F ✅ Composite key when no single field is unique\n\u003CCell key={`${row.id}-${col.name}`} \u002F>\n\n\u002F\u002F ❌ Math.random() — new key every render, defeats the purpose\n\u003CItem key={Math.random()} \u002F>\n\n\u002F\u002F ❌ Index — unstable when list mutates\n\u003CItem key={index} \u002F>\n```\n\nGenerating keys with `Math.random()` or `Date.now()` forces React to\nunmount and remount every item on every render — worse than no key at all.\n\n**Rule of thumb:** use the data's natural ID. If your data has no ID,\ngenerate one at the time the data is created (not at render time).\n",{"id":42,"difficulty":14,"q":43,"a":44},"key-placement","Where does the key prop need to be placed?","The `key` must be on the **outermost element** returned from the map\ncallback — the element React is tracking. It does not need to be (and\ncannot be) read by the component that receives it.\n\n```jsx\n\u002F\u002F ✅ key on the outer element\n{products.map(p => (\n  \u003CProductCard key={p.id} product={p} \u002F>\n))}\n\n\u002F\u002F ❌ key on an inner element — React doesn't see it for list tracking\n{products.map(p => (\n  \u003Cdiv>\n    \u003CProductCard key={p.id} product={p} \u002F>  {\u002F* wrong position *\u002F}\n  \u003C\u002Fdiv>\n))}\n\n\u002F\u002F If the callback returns a Fragment, key goes on the Fragment\n{products.map(p => (\n  \u003CReact.Fragment key={p.id}>\n    \u003Cdt>{p.name}\u003C\u002Fdt>\n    \u003Cdd>{p.description}\u003C\u002Fdd>\n  \u003C\u002FReact.Fragment>\n))}\n```\n\n**Rule of thumb:** key goes on the tag you write in the `.map()` return,\nnot inside it.\n",{"id":46,"difficulty":14,"q":47,"a":48},"key-global-unique","Does the key prop need to be globally unique?","No. Keys only need to be **unique among siblings** — elements within the\nsame list at the same level. The same key value can appear in a different\nlist elsewhere.\n\n```jsx\nfunction Page() {\n  return (\n    \u003C>\n      \u003Cul>\n        {fruits.map(f => \u003Cli key={f.id}>{f.name}\u003C\u002Fli>)}\n      \u003C\u002Ful>\n      \u003Cul>\n        {\u002F* key={1} here doesn't conflict with key={1} above *\u002F}\n        {vegetables.map(v => \u003Cli key={v.id}>{v.name}\u003C\u002Fli>)}\n      \u003C\u002Ful>\n    \u003C\u002F>\n  )\n}\n```\n\nReact tracks keys per list context, not globally across the component tree.\n\n**Rule of thumb:** your IDs only need to be unique within the array you're\nmapping. Composite keys like `${listName}-${item.id}` are redundant unless\nyou have a single flat array mixing different entity types.\n",{"id":50,"difficulty":34,"q":51,"a":52},"reconciliation-keys","What is reconciliation and how do keys affect it?","**Reconciliation** is the process React uses to efficiently update the DOM\nby comparing the new virtual DOM tree with the previous one. React's\ndiffing algorithm has a key heuristic: elements of different types are\nalways replaced; elements of the same type are updated.\n\nKeys extend this heuristic to **lists**: React matches old and new elements\nby key, then decides:\n- Same key, same type → update props in place.\n- New key → mount a new component.\n- Missing key (was present before) → unmount the old component.\n- Same key, different position → React can reorder the DOM node efficiently.\n\n```\nBefore: [A(key=1), B(key=2), C(key=3)]\nAfter:  [C(key=3), A(key=1), B(key=2)]\n\nWith keys: React moves DOM nodes — no unmount\u002Fremount.\nWithout keys: React updates all three by position — potentially wrong.\n```\n\n**Rule of thumb:** correct keys make list operations O(n) instead of\nO(n²); they also prevent state from leaking to the wrong component when\nitems shift.\n",{"id":54,"difficulty":34,"q":55,"a":56},"duplicate-keys","What happens if two siblings have the same key?","React warns in development and exhibits undefined behavior — typically it\nrenders only one of the duplicates and drops the other, or updates the\nwrong component on subsequent renders.\n\n```jsx\n\u002F\u002F ❌ Duplicate keys — unpredictable behavior\n{items.map(item => (\n  \u003CRow key={item.category} data={item} \u002F>\n  \u002F\u002F If multiple items share the same category, keys collide\n))}\n\n\u002F\u002F ✅ Use a truly unique field or compose a unique key\n{items.map(item => (\n  \u003CRow key={item.id} data={item} \u002F>\n))}\n```\n\nIn production, no warning is shown — the bug is silent. This is one reason\nto run development builds regularly.\n\n**Rule of thumb:** if you see the \"Encountered two children with the same\nkey\" warning in the console, your mapping produces non-unique keys — check\nwhich field you're using.\n",{"id":58,"difficulty":34,"q":59,"a":60},"key-prop-readable","Can a component read its own key prop?","No. `key` is **reserved by React** and is not forwarded to the component\nas a prop. If you try to access `props.key` inside the component, you'll\nget `undefined`.\n\n```jsx\nfunction Row({ key, name }) {\n  \u002F\u002F key is always undefined here — React consumed it\n  return \u003Ctr>\u003Ctd>{name}\u003C\u002Ftd>\u003C\u002Ftr>\n}\n\n\u002F\u002F If the component needs the id, pass it explicitly\n{items.map(item => (\n  \u003CRow key={item.id} id={item.id} name={item.name} \u002F>\n))}\n```\n\nThe same is true for `ref` — both are meta-props handled by React before\nthe component sees its props.\n\n**Rule of thumb:** if a component needs its own identifier, pass it as a\nseparate prop (e.g. `id`). Treat `key` as infrastructure, not data.\n",{"id":62,"difficulty":34,"q":63,"a":64},"no-id-data","How do you handle keys when your data has no unique IDs?","Options in rough order of preference:\n\n1. **Add an ID at the source.** If you control the API, add an `id` field.\n2. **Generate IDs when data enters the app** — not at render time.\n\n```jsx\n\u002F\u002F When you receive data, enrich it once\nimport { randomUUID } from 'crypto'\n\nconst items = rawData.map(d => ({ ...d, id: randomUUID() }))\n```\n\n3. **Composite key** from fields that together are unique:\n\n```jsx\n{countries.map(c => (\n  \u003CCountryRow key={`${c.code}-${c.region}`} country={c} \u002F>\n))}\n```\n\n4. **Index as last resort** — only if the list is static (never reordered\n   or mutated) and items have no internal state.\n\n**Rule of thumb:** generate IDs at data-entry time (API response handler,\nform submission), not inside `.map()`. A key generated in `.map()` is a\nnew key every render — React unmounts and remounts every item.\n",{"id":66,"difficulty":34,"q":67,"a":68},"cost-of-no-key","What is the actual cost of not providing a key?","Without a key, React falls back to positional diffing, which causes:\n\n1. **Incorrect state association** — when items reorder, state (including\n   controlled input values, scroll position, open\u002Fclosed accordion) follows\n   the position, not the item.\n\n2. **Unnecessary re-renders and DOM mutations** — React may update the props\n   of an existing component rather than reorder it, doing more work.\n\n3. **Console warning** — in development, `Warning: Each child in a list\n   should have a unique \"key\" prop`.\n\n```jsx\n\u002F\u002F List re-ordered from [A, B, C] to [C, A, B]\n\u002F\u002F Without keys: React updates A→C, B→A, C→B (three updates + DOM writes)\n\u002F\u002F With keys: React moves DOM nodes (one reorder)\n```\n\nThe correctness bug is worse than the performance bug: stale input values\nor wrong selection state are visible to users.\n\n**Rule of thumb:** treat missing keys as a bug, not a warning. Fix them\nbefore they reach production.\n",{"id":70,"difficulty":34,"q":71,"a":72},"render-component-list","How do you render a list of different component types based on data?","Use an object map or a switch inside the map callback to select the\ncomponent type dynamically.\n\n```jsx\nconst BLOCK_COMPONENTS = {\n  text:    TextBlock,\n  image:   ImageBlock,\n  video:   VideoBlock,\n  divider: DividerBlock,\n}\n\nfunction PageContent({ blocks }) {\n  return (\n    \u003Carticle>\n      {blocks.map(block => {\n        const Block = BLOCK_COMPONENTS[block.type]\n        if (!Block) return null\n        return \u003CBlock key={block.id} {...block} \u002F>\n      })}\n    \u003C\u002Farticle>\n  )\n}\n```\n\nNote that `Block` (uppercase) is required because JSX uses the case to\ndistinguish between a DOM element (string) and a component (variable reference).\n\n**Rule of thumb:** the object-map pattern scales better than a switch as\nthe number of types grows, and makes it easy to add new block types in one\nplace.\n",{"id":74,"difficulty":34,"q":75,"a":76},"index-reorder-bug","What happens when list items reorder and you used index keys?","React matches old element at index N to new element at index N. If items\nreorder, the component at each position gets the props of the new item but\n**retains the state of the old item** — because from React's perspective\nit's the \"same\" component (same key = same index).\n\n```jsx\n\u002F\u002F Scenario: [Alice, Bob, Carol] → [Carol, Alice, Bob] (sorted)\n\u002F\u002F With index keys:\n\u002F\u002F   Index 0 was Alice, now Carol → React updates props: Alice→Carol\n\u002F\u002F   But Alice's text input still shows Alice's draft (state is index-0's)\n```\n\nVisually: the displayed name changes, but any controlled input value,\nopen dropdown, or animation state stays — it belongs to the position, not\nthe item.\n\n**Rule of thumb:** this is the canonical \"index key\" bug. Any time a list\ncan be sorted, filtered, or have items added\u002Fremoved, use stable IDs.\n",{"id":78,"difficulty":34,"q":79,"a":80},"acceptable-index-key","When is it acceptable to use index as a key?","The React team's guidance: index as key is safe only when **all three**\nconditions hold:\n1. The list is **static** — it never reorders or has items inserted\u002Fdeleted.\n2. Items have **no stable IDs** in the data.\n3. The list is **never re-filtered** — count and order don't change.\n\n```jsx\n\u002F\u002F Acceptable: read-only, ordered, no unique id, no internal state\nconst STEPS = ['Install', 'Configure', 'Deploy']\n{STEPS.map((step, i) => (\n  \u003Cli key={i}>{step}\u003C\u002Fli>\n))}\n```\n\nIf the list comes from an API and may change at any time, don't use index.\n\n**Rule of thumb:** if you have to think about whether index is safe, your\ndata should have IDs. The safe scenarios are narrow; don't give yourself\npermission to use index keys broadly.\n",{"id":82,"difficulty":34,"q":83,"a":84},"nested-lists","How do nested lists affect key requirements?","Each level of nesting is its own sibling context. Keys at one level don't\ninteract with keys at another. However, every `.map()` at every level\nneeds its own `key`.\n\n```jsx\nfunction Table({ rows }) {\n  return (\n    \u003Ctable>\n      \u003Ctbody>\n        {rows.map(row => (\n          \u003Ctr key={row.id}>   {\u002F* key for row siblings *\u002F}\n            {row.cells.map(cell => (\n              \u003Ctd key={cell.colId}>{cell.value}\u003C\u002Ftd>  {\u002F* key for cell siblings *\u002F}\n            ))}\n          \u003C\u002Ftr>\n        ))}\n      \u003C\u002Ftbody>\n    \u003C\u002Ftable>\n  )\n}\n```\n\n**Rule of thumb:** every `.map()` call needs a `key`, at every nesting\nlevel. The keys only need to be unique among siblings at that level —\n`row.id` and `cell.colId` don't need to be in different namespaces.\n",{"id":86,"difficulty":34,"q":87,"a":88},"key-vs-ref","What is the difference between key and ref in React?","Both are special \"meta-props\" handled by React before the component sees\nits props, but they serve completely different purposes:\n\n| | `key` | `ref` |\n|---|---|---|\n| Purpose | List identity for reconciliation | Imperative access to DOM node \u002F component instance |\n| Readable by component | No (`props.key` is `undefined`) | Via `useRef` \u002F `forwardRef` |\n| When to use | Every element in a `.map()` list | Direct DOM manipulation, focus management, measuring |\n| Value type | String or number | Object from `useRef()` or callback |\n\n```jsx\nconst inputRef = useRef()\n\n\u002F\u002F key for list tracking, ref for focus imperatively\n{items.map(item => (\n  \u003Cinput key={item.id} ref={item.isFocused ? inputRef : null} \u002F>\n))}\n```\n\n**Rule of thumb:** `key` is for React's reconciler (list diffing); `ref`\nis for your code to poke the DOM. They are unrelated features that happen\nto both be invisible to `props`.\n",{"id":90,"difficulty":34,"q":91,"a":92},"fragment-in-lists","What is the role of React.Fragment in lists?","When a list item needs to render **multiple sibling elements** without a\nwrapper DOM node, use `\u003CReact.Fragment key={...}>` — the short `\u003C>…\u003C\u002F>`\nsyntax doesn't support `key`.\n\n```jsx\nfunction DefinitionList({ items }) {\n  return (\n    \u003Cdl>\n      {items.map(item => (\n        \u002F\u002F Short fragment syntax doesn't accept key — use long form\n        \u003CReact.Fragment key={item.id}>\n          \u003Cdt>{item.term}\u003C\u002Fdt>\n          \u003Cdd>{item.definition}\u003C\u002Fdd>\n        \u003C\u002FReact.Fragment>\n      ))}\n    \u003C\u002Fdl>\n  )\n}\n```\n\nIf you wrap in a `\u003Cdiv>` instead, the resulting `\u003Cdl>\u003Cdiv>\u003Cdt>…\u003Cdd>…\u003C\u002Fdiv>\u003C\u002Fdl>`\nis invalid HTML and breaks styling.\n\n**Rule of thumb:** whenever you need `key` on a Fragment, use\n`\u003CReact.Fragment key={id}>`. It's the only valid way to key a fragment.\n",{"id":94,"difficulty":95,"q":96,"a":97},"virtual-dom-diffing","hard","How does React's virtual DOM use keys for diffing list elements?","React's reconciliation algorithm compares the previous and next virtual DOM\ntrees in a single pass per level. For lists specifically, it builds a\n**key-to-fiber map** of the old children, then for each child in the new\nlist it looks up the key:\n\n- **Key found, same type** → update props, keep the existing fiber (DOM\n  node), move it if position changed.\n- **Key found, different type** → unmount old, mount new.\n- **Key not found** → mount new fiber.\n- **Old key no longer in new list** → unmount.\n\n```\nOld: [\u003Cli key=\"a\">, \u003Cli key=\"b\">, \u003Cli key=\"c\">]\nNew: [\u003Cli key=\"c\">, \u003Cli key=\"a\">, \u003Cli key=\"b\">]\n\nReact: key \"c\" moved from index 2 → 0, DOM node reused.\n       keys \"a\" and \"b\" shifted, DOM nodes reused.\nResult: 0 unmounts, 0 mounts, 3 DOM moves.\n```\n\nWithout keys React would compare index-by-index and update all three\nelements' props even though the content is the same.\n\n**Rule of thumb:** keys let React reorder DOM nodes cheaply instead of\ndoing redundant prop patches. This is why keys are a performance AND\ncorrectness feature.\n",18,null,{"description":11},"React lists and keys interview questions — key prop, reconciliation, index as key pitfalls, stable keys, Fragment in lists, and rendering nested arrays.","react\u002Fcomponents\u002Flists-keys","Lists and Keys","Components","components","2026-06-23","PqeIWQPL16QElLjRR0piFnipmLYPvjvm8nl6oVP4PTU",[109,113,116,120,124],{"subtopic":110,"path":111,"order":112},"JSX and Rendering","\u002Freact\u002Fcomponents\u002Fjsx-rendering",1,{"subtopic":114,"path":115,"order":12},"Props and Component Types","\u002Freact\u002Fcomponents\u002Fprops-component-types",{"subtopic":117,"path":118,"order":119},"Event Handling","\u002Freact\u002Fcomponents\u002Fevent-handling",3,{"subtopic":121,"path":122,"order":123},"Conditional Rendering","\u002Freact\u002Fcomponents\u002Fconditional-rendering",4,{"subtopic":103,"path":22,"order":21},{"path":126,"title":127},"\u002Fblog\u002Freact-lists-keys-guide","React Lists and Keys — A Complete Interview Guide",1782244100890]