Skip to the solution
EasyFrontend6 min read

What is the purpose of the key prop when rendering lists in React?

891 views

The problem

Question presented to candidate: "Why does React want a key on list items, and what actually goes wrong without a stable one?"

What a strong answer should cover:

  • A key gives each list child a stable identity across renders, so React can tell whether an item moved, was added, or was removed β€” rather than comparing by position.
  • Without keys React falls back to index, which is correct only if the list never reorders, has nothing inserted at the front, and nothing removed from the middle.
  • The concrete failure: state and DOM attach to the wrong item. Uncontrolled input values, focus, scroll position, and component state all follow the key, not the data.
  • Index keys are acceptable for a static list that never changes order β€” and genuinely wrong the moment it can.
  • Keys must be stable, unique among siblings, and predictable. Not Math.random(), which remounts everything every render.
  • Keys are not a prop β€” key is compiled to a separate argument, so a component cannot read its own key.
  • The flip side: deliberately changing a key resets state, which is the idiomatic way to reset a component on a prop change.
  • Keys only need to be unique among siblings, not globally.

Clarifying questions expected:

  • "Can this list reorder, or have items inserted or removed from anywhere but the end?"
  • "Do the items have a stable id from the server?"

Code / implementation expected: Yes β€” a reorderable list with inputs, showing index keys mismatching.

  • lists
  • keys
  • performance
  • reconciliation

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

6 min read Β· 10 sections

Try it in the playground

Open any example in the playground to run and change it.

ReactThe same list keyed by index and by id β€” reorder it and compare
import { useState } from "react";

// Each row owns state (the note) AND has an uncontrolled-ish feel: whatever you
// type follows the KEY, not the data. That is the whole demonstration.
function Row({ person, tone }) {
  const [note, setNote] = useState("");
  return (
    <li style={{ display: "flex", gap: 8, alignItems: "center", padding: "3px 0" }}>
      <span style={{ minWidth: 70, color: tone }}>{person.name}</span>
      <input
        value={note}
        placeholder="type a note"
        onChange={(e) => setNote(e.target.value)}
        style={{ flex: 1 }}
      />
    </li>
  );
}

const INITIAL = [
  { id: "a", name: "Ada" },
  { id: "g", name: "Grace" },
  { id: "t", name: "Alan" },
];

export default function App() {
  const [people, setPeople] = useState(INITIAL);

  const reverse = () => setPeople((p) => [...p].reverse());
  const removeFirst = () => setPeople((p) => p.slice(1));
  const reset = () => setPeople(INITIAL);

  return (
    <div style={{ padding: 24, fontFamily: "system-ui", lineHeight: 1.6, maxWidth: 520 }}>
      <p style={{ fontSize: 14 }}>
        Type a note into the <strong>first row of each list</strong>, then press
        Reverse. Watch which name the note follows.
      </p>

      <section style={{ border: "2px solid #dc2626", borderRadius: 8, padding: 12, marginBottom: 12 }}>
        <h4 style={{ margin: "0 0 6px", color: "#dc2626" }}>❌ key={"{index}"}</h4>
        <ul style={{ listStyle: "none", padding: 0, margin: 0 }}>
          {people.map((p, i) => (
            // The key is the POSITION, so state stays with the position.
            <Row key={i} person={p} tone="#dc2626" />
          ))}
        </ul>
      </section>

      <section style={{ border: "2px solid #16a34a", borderRadius: 8, padding: 12 }}>
        <h4 style={{ margin: "0 0 6px", color: "#16a34a" }}>βœ… key={"{person.id}"}</h4>
        <ul style={{ listStyle: "none", padding: 0, margin: 0 }}>
          {people.map((p) => (
            // The key belongs to the DATA, so state follows the person.
            <Row key={p.id} person={p} tone="#16a34a" />
          ))}
        </ul>
      </section>

      <p style={{ marginTop: 12 }}>
        <button onClick={reverse}>reverse</button>{" "}
        <button onClick={removeFirst} disabled={people.length === 0}>remove first</button>{" "}
        <button onClick={reset}>reset</button>
      </p>

      <p style={{ color: "#666", fontSize: 13 }}>
        In the red list the note stays on row one whoever is standing there. In
        the green list it travels with the person. Nothing warns you β€” which is
        exactly what makes index keys dangerous.
      </p>
    </div>
  );
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 8 of 119 in React.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All React.js questions
Up nextEasy

Explain the concept of 'lifting state up' in React.

Continue