Skip to solution
easyFrontend

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

891 views
01

Understand 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 propkey 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.

listskeysperformancereconciliation
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for React interviews — assumes rendering lists. Difficulty: Easy to Medium

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. The mismatch in section 3 was produced by typing into a real input and th

Solution ready — 2 min read

Classified // press E to declassify

04

Explore the playground snippets

The same list keyed by index and by id — reorder it and compare
Run Playground
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>
  );
}
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 8 of 119 decoded in the React.js track. One more won't hurt.

Back to track